用于点击跟踪的高阶 React 组件
Higher Order React Component for Click Tracking
我想实现一个更高阶的 React 组件,它可以用来轻松跟踪任何 React 组件上的事件(如点击)。这样做的目的是轻松地将点击(和其他事件)连接到我们的第一方分析跟踪器中。
我遇到的挑战是 React 合成事件系统需要事件(如 onClick
)必须对 DOM 元素做出反应,如 div
。如果我包装的组件是自定义组件,就像通过高阶函数实现的每个 HOC 一样,我的点击事件不会正确绑定。
例如,使用此 HOC,onClick
处理程序将为按钮 1 触发,但不会为按钮 2 触发。
// Higher Order Component
class Track extends React.Component {
onClick = (e) => {
myTracker.track(props.eventName);
}
render() {
return React.Children.map(
this.props.children,
c => React.cloneElement(c, {
onClick: this.onClick,
}),
);
}
}
function Wrapper(props) {
return props.children;
}
<Track eventName={'button 1 click'}>
<button>Button 1</button>
</Track>
<Track eventName={'button 2 click'}>
<Wrapper>
<button>Button 2</button>
</Wrapper>
</Track>
CodeSandbox 与工作示例: https://codesandbox.io/embed/pp8r8oj717
我的目标是能够使用像这样的 HOF(可选地作为装饰器)来跟踪对任何 React 组件定义的点击。
export const withTracking = eventName => Component => props => {
return (
<Track eventName={eventName}>
{/* Component cannot have an onClick event attached to it */}
<Component {...props} />
</Track>
);
};
我能想到的唯一解决方案是在每个 child 上使用 Ref
并在填充 Ref
后手动绑定我的点击事件。
如有任何想法或其他解决方案,我们将不胜感激!
更新:
使用@estus 的回答中的 remapChildren
技术和一种更手动的渲染包装组件的方法,我能够让它作为高阶函数工作 - https://codesandbox.io/s/3rl9rn1om1
export const withTracking = eventName => Component => {
if (typeof Component.prototype.render !== "function") {
return props => <Track eventName={eventName}>{Component(props)}</Track>;
}
return class extends Component {
render = () => <Track eventName={eventName}>{super.render()}</Track>;
};
};
不可能通过常规方式从 Wrapper
React 元素中获取对底层 DOM 节点的引用(并且可以有多个节点),例如 ref 或 ReactDOM.findDOMNode
.为此,应递归遍历子元素。
一个example:
remapChildren(children) {
const { onClick } = this;
return React.Children.map(
children,
child => {
if (typeof child.type === 'string') {
return React.cloneElement(child, { onClick });
} else if (React.Children.count(child.props.children)) {
return React.cloneElement(child, { children: this.remapChildren(
child.props.children
) });
}
}
);
}
render() {
return this.remapChildren(this.props.children);
}
remapChildren
仅将 onClick
放置在 DOM 元素上。请注意,此实现会跳过嵌套的 DOM 元素。
我想实现一个更高阶的 React 组件,它可以用来轻松跟踪任何 React 组件上的事件(如点击)。这样做的目的是轻松地将点击(和其他事件)连接到我们的第一方分析跟踪器中。
我遇到的挑战是 React 合成事件系统需要事件(如 onClick
)必须对 DOM 元素做出反应,如 div
。如果我包装的组件是自定义组件,就像通过高阶函数实现的每个 HOC 一样,我的点击事件不会正确绑定。
例如,使用此 HOC,onClick
处理程序将为按钮 1 触发,但不会为按钮 2 触发。
// Higher Order Component
class Track extends React.Component {
onClick = (e) => {
myTracker.track(props.eventName);
}
render() {
return React.Children.map(
this.props.children,
c => React.cloneElement(c, {
onClick: this.onClick,
}),
);
}
}
function Wrapper(props) {
return props.children;
}
<Track eventName={'button 1 click'}>
<button>Button 1</button>
</Track>
<Track eventName={'button 2 click'}>
<Wrapper>
<button>Button 2</button>
</Wrapper>
</Track>
CodeSandbox 与工作示例: https://codesandbox.io/embed/pp8r8oj717
我的目标是能够使用像这样的 HOF(可选地作为装饰器)来跟踪对任何 React 组件定义的点击。
export const withTracking = eventName => Component => props => {
return (
<Track eventName={eventName}>
{/* Component cannot have an onClick event attached to it */}
<Component {...props} />
</Track>
);
};
我能想到的唯一解决方案是在每个 child 上使用 Ref
并在填充 Ref
后手动绑定我的点击事件。
如有任何想法或其他解决方案,我们将不胜感激!
更新:
使用@estus 的回答中的 remapChildren
技术和一种更手动的渲染包装组件的方法,我能够让它作为高阶函数工作 - https://codesandbox.io/s/3rl9rn1om1
export const withTracking = eventName => Component => {
if (typeof Component.prototype.render !== "function") {
return props => <Track eventName={eventName}>{Component(props)}</Track>;
}
return class extends Component {
render = () => <Track eventName={eventName}>{super.render()}</Track>;
};
};
不可能通过常规方式从 Wrapper
React 元素中获取对底层 DOM 节点的引用(并且可以有多个节点),例如 ref 或 ReactDOM.findDOMNode
.为此,应递归遍历子元素。
一个example:
remapChildren(children) {
const { onClick } = this;
return React.Children.map(
children,
child => {
if (typeof child.type === 'string') {
return React.cloneElement(child, { onClick });
} else if (React.Children.count(child.props.children)) {
return React.cloneElement(child, { children: this.remapChildren(
child.props.children
) });
}
}
);
}
render() {
return this.remapChildren(this.props.children);
}
remapChildren
仅将 onClick
放置在 DOM 元素上。请注意,此实现会跳过嵌套的 DOM 元素。