Компонент реакции высшего порядка для отслеживания кликов - PullRequest
0 голосов
/ 08 октября 2018

Я хотел бы реализовать компонент реакции более высокого порядка, который можно использовать для простого отслеживания событий (например, щелчка) в любом компоненте React.Цель этого состоит в том, чтобы легко подключать клики (и другие события) к нашему первому стороннему аналитическому трекеру.

Проблема, с которой я столкнулся, состоит в том, что синтетическая система событий React требует, чтобы события (такие как onClick) былиобязан реагировать на элементы DOM, как div.Если компонент, который я обертываю, является пользовательским компонентом, как и любой HOC, реализованный с помощью функции более высокого порядка, мои события щелчка не связываются правильно.

Например, при использовании этого HOC обработчик onClickбудет срабатывать для button1, но не для 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.

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

Единственное решение, которое я могу придумать, - использовать Ref для каждого дочернего элемента и вручную связывать мое событие click, как толькоRef заполнено.

Любые идеи или другие решения приветствуются!

ОБНОВЛЕНИЕ: Использование метода remapChildren из ответа @estus и более ручной способрендеринга обернутых компонентов, я смог заставить это работать как функция более высокого порядка - 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>;
  };
};

1 Ответ

0 голосов
/ 08 октября 2018

Невозможно получить ссылку на базовый узел DOM (а их может быть больше одного) из Wrapper Реагировать на элемент обычными средствами, например, ref или ReactDOM.findDOMNode.Чтобы сделать это, дочерние элементы должны быть рекурсивно пройдены.

Пример :

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.

...