Я хотел бы реализовать компонент реакции более высокого порядка, который можно использовать для простого отслеживания событий (например, щелчка) в любом компоненте 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>;
};
};