用于点击跟踪的高阶反应组件 [英] Higher Order React Component for Click Tracking

查看:91
本文介绍了用于点击跟踪的高阶反应组件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'd like to implement a higher order react component which can be used to easily track events (like a click) on any React component. The purpose of this is to easily hook clicks (and other events) into our first party analytics tracker.

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

The challenge I've come across is that the React synthetic event system requires events (like onClick) be bound to react DOM elements, like a div. If the component I'm wrapping is a custom component, like every HOC implemented via a higher order function is, my click events don't bind correctly.

例如,使用此HOC, onClick 处理程序将触发button1,但不会触发button2。

For example, using this HOC, the onClick handler will fire for button1, but not for button2.

// 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组件定义的点击。

My goal is to be able to use an HOF like this (optionally as a decorator) to track clicks on any React component definition.

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

我能想到的唯一解决方案是使用参考对每个孩子进行操作,并在填充 Ref 后手动绑定我的点击事件。

The only solution I can think of atm is using a Ref on each child and manually binding my click event once the Ref is populated.

任何想法或其他解决方案表示赞赏!

Any ideas or other solutions are appreciated!

更新:
使用 remapChildren 技术从@estus的回答和更加手工的渲染包装组件的方式来看,我能够将其作为更高阶的函数 - https://codesandbox.io/s/3rl9rn1om1

UPDATE: Using the remapChildren technique from @estus' answer and a more manual way of rendering the wrapped components, I was able to get this to work as a higher order function - 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>;
  };
};


推荐答案

获取对底层DOM节点的引用是不可能的(并且可以有多个来自 Wrapper 通过常规方式的React元素,如ref或 ReactDOM.findDOMNode 。为了做到这一点,元素子元素应该递归遍历。

It's impossible to get a reference to underlying DOM node (and there can be more than one of them) from Wrapper React element by regular means, like a ref or ReactDOM.findDOMNode. In order to do that, element children should be traversed recursively.

示例

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 places onClick 仅适用于DOM元素。请注意,此实现会跳过嵌套的DOM元素。

remapChildren places onClick only on DOM elements. Notice that this implementation skips nested DOM elements.

这篇关于用于点击跟踪的高阶反应组件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆