Javascript 如何为整个应用程序创建包装器组件?
我正在尝试为我的react应用程序添加一些分析跟踪。基本上只是使用组件添加全局事件侦听器,然后在该组件中适当地处理事件 我想将我的整个应用程序包装在此组件中,并让它拾取Javascript 如何为整个应用程序创建包装器组件?,javascript,reactjs,Javascript,Reactjs,我正在尝试为我的react应用程序添加一些分析跟踪。基本上只是使用组件添加全局事件侦听器,然后在该组件中适当地处理事件 我想将我的整个应用程序包装在此组件中,并让它拾取组件将更新道具更改,以便我可以使用道具位置对页面更改做出反应。我的问题是我不知道如何设置我的包装器组件来实现这一点。我知道HOC的概念有助于包装一个组件,我已经测试过它是否有效,但我希望它是一个更通用、更全局的组件 Tracker.js import PropTypes from "prop-types" import * as
组件将更新道具更改,以便我可以使用道具位置对页面更改做出反应。我的问题是我不知道如何设置我的包装器组件来实现这一点。我知道HOC的概念有助于包装一个组件,我已经测试过它是否有效,但我希望它是一个更通用、更全局的组件
Tracker.js
import PropTypes from "prop-types"
import * as React from "react"
import { connect } from "react-redux"
import TrackingManager from './TrackingManager'
import ScriptManager from "./ScriptManager"
import { isLeftClickEvent } from "../utils/Utils"
const trackingManager = new TrackingManager()
const config = {
useTagManager: true,
tagManagerAccount: 'testCccount',
tagManagerProfile: 'testProfile',
tagManagerEnvironment: 'dev'
}
/**
* compares the locations of 2 components, mostly taken from:
* http://github.com/nfl/react-metrics/blob/master/src/react/locationEquals.js
*
* @param a
* @param b
* @returns {boolean}
*/
function locationEquals(a, b) {
if (!a && !b) {
return true;
}
if ((a && !b) || (!a && b)) {
return false;
}
return (
a.pathname === b.pathname && a.search === b.search && a.state === b.state
);
}
/**
* Tracking container which wraps the supplied Application component.
* @param Application
* @param beforeAction
* @param overrides
* @returns {object}
*/
const track = Application =>
class TrackingContainer extends React.Component {
constructor(props) {
super(props)
}
componentDidMount() {
this._addClickListener()
this._addSubmitListener()
}
componentWillUnmount() {
// prevent side effects by removing listeners upon unmount
this._removeClickListener()
this._removeSubmitListener()
}
componentDidUpdate(prevProps) {
// if and only if the location has changed we need to track a
// new pageview
if (!locationEquals(this.props.location, prevProps.location)) {
this._handlePageView(this.props)
}
}
_addClickListener = () => {
// bind to body to catch clicks in portaled elements (modals, tooltips, dropdowns)
document.body.addEventListener("click", this._handleClick)
}
_removeClickListener = () => {
document.body.removeEventListener("click", this._handleClick)
}
_addSubmitListener = () => {
document.body.addEventListener("submit", this._handleSubmit)
}
_removeSubmitListener = () => {
document.body.removeEventListener("submit", this._handleSubmit)
}
_handleSubmit = event => {
console.log(event.target.name)
}
_handleClick = event => {
// ensure the mouse click is an event we're interested in processing,
// we have discussed limiting to external links which go outside the
// react application and forcing implementers to use redux actions for
// interal links, however the app is not implemented like that in
// places, eg: Used Search List. so we're not enforcing that restriction
if (!isLeftClickEvent(event)) {
return
}
// Track only events when triggered from a element that has
// the `analytics` data attribute.
if (event.target.dataset.analytics !== undefined) {
trackingManager.event('pageName', 'User')
}
}
_handlePageView = route => {
console.log('CHANGE PAGE EVENT')
console.log(route)
}
/**
* Return tracking script.
*/
_renderTrackingScript() {
/**
* If utag is already loaded on the page we don't want to load it again
*/
if (window.utag !== undefined) return
if (config.useTagManager === false) return
/**
* Load utag script.
*/
return (
<ScriptManager
account={config.tagManagerAccount}
profile={config.tagManagerProfile}
environment={config.tagManagerEnvironment}
/>
)
}
render() {
return (
<React.Fragment>
<Application {...this.props} {...this.state} />
{this.props.children}
{this._renderTrackingScript()}
</React.Fragment>
)
}
}
export default track
从“道具类型”导入道具类型
从“React”导入*作为React
从“react redux”导入{connect}
从“./TrackingManager”导入TrackingManager
从“/ScriptManager”导入ScriptManager
从“./utils/utils”导入{isLeftClickEvent}
const trackingManager=new trackingManager()
常量配置={
是的,
tagManagerAccount:'testCccount',
tagManagerProfile:'testProfile',
tagManagerEnvironment:'dev'
}
/**
*比较两个组件的位置,主要取自:
* http://github.com/nfl/react-metrics/blob/master/src/react/locationEquals.js
*
*@param a
*@param b
*@returns{boolean}
*/
函数位置等于(a,b){
如果(!a&&!b){
返回true;
}
如果((a&&b)|(!a&&b)){
返回false;
}
返回(
a、 路径名===b.pathname&&a.search==b.search&&a.state===b.state
);
}
/**
*包装提供的应用程序组件的跟踪容器。
*@param应用程序
*@param-beforeAction
*@param覆盖
*@返回{object}
*/
const track=应用程序=>
类TrackingContainer扩展了React.Component{
建造师(道具){
超级(道具)
}
componentDidMount(){
这是。_addClickListener()
这是.\u addSubmitListener()
}
组件将卸载(){
//通过在卸载时删除侦听器来防止副作用
此。_removeClickListener()
此._removeSubmitListener()
}
componentDidUpdate(prevProps){
//如果且仅当位置发生变化,我们需要跟踪
//新页面视图
如果(!locationEquals(this.props.location,prevProps.location)){
此.\u handlePageView(此.props)
}
}
_addClickListener=()=>{
//绑定到body以捕捉portaled元素(模态、工具提示、下拉列表)中的单击
document.body.addEventListener(“单击”,此.\u handleClick)
}
_removeClickListener=()=>{
document.body.removeEventListener(“单击”,此.\u handleClick)
}
_addSubmitListener=()=>{
document.body.addEventListener(“提交”,本._handleSubmit)
}
_removeSubmitListener=()=>{
document.body.removeEventListener(“提交”,本.\u handleSubmit)
}
_handleSubmit=事件=>{
console.log(event.target.name)
}
_handleClick=事件=>{
//确保鼠标点击是我们感兴趣处理的事件,
//我们已经讨论了限制外部链接的问题
//react应用程序并强制实现者使用redux操作
//内部链接,但该应用程序并不像在中那样实现
//位置,例如:已使用的搜索列表。所以我们不强制执行该限制
如果(!isLeftClickEvent(事件)){
返回
}
//仅当从具有以下属性的元素触发时跟踪事件:
//“分析”数据属性。
if(event.target.dataset.analytics!==未定义){
trackingManager.event('pageName','User')
}
}
_handlePageView=路线=>{
console.log('更改页面事件')
控制台日志(路由)
}
/**
*返回跟踪脚本。
*/
_renderTrackingScript(){
/**
*如果页面上已经加载了utag,我们不想再次加载它
*/
如果(window.utag!==未定义)返回
如果(config.useTagManager==false)返回
/**
*加载utag脚本。
*/
返回(
)
}
render(){
返回(
{this.props.children}
{this.\u renderTrackingScript()}
)
}
}
导出默认轨迹
使用我的index.js,我想做类似的事情:
import React from 'react'
import ReactDOM from 'react-dom'
import { Router, Switch, Route } from 'react-router-dom'
import { Provider } from 'react-redux'
import store from './lib/store'
import history from './lib/history'
import Loadable from 'react-loadable'
import PageLoader from './components/PageLoader/PageLoader'
import {
DEFAULT_PATH,
LOGIN_PATH,
LOGOUT_PATH,
USER_PATH,
} from './lib/paths'
const Login = Loadable({ loader: () => import('./scenes/Auth/Login' /* webpackChunkName: 'login' */), loading: PageLoader })
const Logout = Loadable({ loader: () => import('./scenes/Auth/Logout'/* webpackChunkName: 'logout' */), loading: PageLoader })
const User = Loadable({ loader: () => import('./scenes/Auth/User'/* webpackChunkName: 'user' */), loading: PageLoader })
import Track from './lib/tracking/Tracker'
import './assets/stylesheets/bootstrap.scss'
import './bootstrap-ds.css'
import './index.css'
import './assets/stylesheets/scenes.scss'
ReactDOM.render((
// This is an example of what I want to accomplish
<Track>
<Provider store={store}>
<Router history={history}>
<Switch>
<Route path={LOGIN_PATH} component={Login} />
<Route path={LOGOUT_PATH} component={Logout} />
<Route path={USER_PATH} component={User} />
</Switch>
</Router>
</Provider>
</Track>
), document.getElementById('root'))
从“React”导入React
从“react dom”导入react dom
从“react Router dom”导入{Router,Switch,Route}
从“react redux”导入{Provider}
从“./lib/store”导入存储
从“/lib/history”导入历史记录
从“react Loadable”导入可加载
从“./components/PageLoader/PageLoader”导入页面加载器
导入{
默认路径,
登录路径,
注销路径,
用户路径,
}来自“./lib/path”
const Login=Loadable({loader:()=>import('./scenes/Auth/Login'/*webpackChunkName:'Login'*/),loading:PageLoader})
const Logout=Loadable({loader:()=>import('./scenes/Auth/Logout'/*webpackChunkName:'Logout'*/),加载:PageLoader})
const User=Loadable({loader:()=>import('./scenes/Auth/User'/*webpackChunkName:'User'*/),loading:PageLoader})
从“./lib/tracking/Tracker”导入轨迹
导入“./assets/stylesheets/bootstrap.scss”
导入“./bootstrap ds.css”
导入“./index.css”
导入“./assets/stylesheets/sces.scss”
ReactDOM.render((
//这是我想要完成的一个例子
),document.getElementById('root'))
因此,基本上,
组件可以包装整个应用程序,仍然使用道具并检查它们是否更新。有办法做到这一点吗?我需要更改什么?这里似乎是您的用例。您需要一种解耦的方式在同一树中的组件之间共享数据。您的包装器可以实现一个提供者
,所有对共享值感兴趣的组件都将实现一个使用者const { Provider, Consumer } = React.createContext()
const Wrapper = ({children}) =>{
return(
<Provider value={mySharedValue}>
{children}
</Provider>
)
}
const NestedChildren = () =>{
return(
<Consumer>
{context => <div>{context}</div>}
</Consumer>
)
}
const App = () =>{
return(
<Wrapper>
<Child> <NestedChild /> </Child>
</Wrapper>
)
}
import { MetricsProvider } from 'react-capture-metrics'
const analytics = {
track: (name, properties) => window.analytics.track(name, properties),
page: (name, properties, category) => window.analytics.page(...(category ? [category, name, properties] : [name, properties]))
}
function App () {
return (
<MetricsProvider analytics={analytics} properties={{ appVersion: pkg.version }}>
// ...the rest of your app
</MetricsProvider>
)
}
function Page() {
const { PageView } = useMetrics({
variantId,
// ...properties to capture
}, { ready: variantId !== undefined })
return (
<PageView
name="Home"
category="Customer"
ready={/* some useState value perhaps */ }
>
// ...
</PageView>
)
}