用于点击跟踪的高阶React组件

时间:2018-10-08 15:25:40

标签: javascript reactjs javascript-events high-order-component

我想实现一个更高阶的react组件,该组件可用于轻松跟踪任何React组件上的事件(例如单击)。这样做的目的是轻松将点击(和其他事件)吸引到我们的第一方分析跟踪器中。

我遇到的挑战是React合成事件系统要求事件(如div)必须绑定以响应DOM元素(如onClick)。如果我要包装的组件是自定义组件,就像每个通过高阶函数实现的HOC一样,我的click事件将无法正确绑定。

例如,使用此HOC,// 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> 处理程序将为button1触发,但不会为button2触发。

export const withTracking = eventName => Component => props => {
  return (
    <Track eventName={eventName}>
      {/* Component cannot have an onClick event attached to it */}
      <Component {...props} />
    </Track>
  );
};

带有示例的CodeSandbox: https://codesandbox.io/embed/pp8r8oj717

我的目标是能够使用像这样的HOF(可选地作为装饰器)来跟踪任何React组件定义上的点击。

Ref

我能想到的唯一解决方案是在每个孩子上使用Ref,并在填充remapChildren后手动绑定我的点击事件。

感谢任何想法或其他解决方案!

更新: 使用@estus回答中的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>; }; }; 技术和一种更手动的方式来呈现已包装的组件,我能够使它作为高阶函数工作-https://codesandbox.io/s/3rl9rn1om1

BigDecimal

1 个答案:

答案 0 :(得分:1)

通过常规方法,例如ref或Wrapper,不可能从ReactDOM.findDOMNode React元素获得对基础DOM节点的引用(并且可以有多个)。为此,应递归遍历子元素。

一个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元素。