我想添加分析跟踪事件到我的应用程序中的所有按钮。所有的按钮都是mui
按钮,但我觉得我的问题将是相同的,如果我只是使用原生HTMLbutton
s。
我的想法是创建一个"包装器"。我用react组件替换所有Button
组件,它基本上包装了Button
组件,向其事件侦听器添加了一些进一步的逻辑,然后继续传播所附加的侦听器。
我已经达到了一个点,我不确定如何最好地做到这一点,同时也转发ref
s。问题可能是"我如何同时转发参考和使用本地createRef
参考,虽然我不确定。">
组件如下所示:
CustomButton.tsx
import _ from 'lodash';
import { Button, ButtonProps } from '@mui/material';
import { analyticsTrack, mergeRefs } from 'utils/functions';
import { forwardRef, ForwardedRef, createRef } from 'react';
interface CustomButtonProps extends ButtonProps {
trackingName?: string;
}
const CustomButton = forwardRef(
(
{ trackingName, children, ...rest }: CustomButtonProps,
ref: ForwardedRef<any>
) => {
const buttonRef = createRef();
function handleClick(...args: any) {
analyticsTrack('Button Clicked', {
props: rest,
ariaLabel: rest['aria-label'],
trackingName,
buttonText: typeof children === 'string' ? children : '',
});
if (rest.onClick) {
rest.onClick(args);
}
}
return (
// @ts-ignore
// eslint-disable-next-line react/jsx-no-bind
<Button
{..._.omitBy(rest, ['onClick'])}
onMouseDown={(event) => event.stopPropagation()}
onTouchStart={(event) => event.stopPropagation()}
onClick={(event) => {
event.stopPropagation();
event.preventDefault();
handleClick();
// @ts-ignore
buttonRef.current && buttonRef.current.dispatchEvent(event);
}}
// @ts-ignore
ref={mergeRefs(ref, buttonRef)}
>
{children}
</Button>
);
}
);
export default CustomButton;
我对类型也很困惑,不太确定为什么ForwardRef
,Ref.
和RefObject
的类型之间存在差异。mergeRefs
函数如下所示:
export const mergeRefs = (...refs: RefObject<Element | null>[]) => {
return ( node: Element ) => {
for (const ref of refs) {
// @ts-ignore
ref && !ref.current ? ref.current = node : null;
}
}
}
我已经阅读了react文档上的forwardRefs
页面,这让我相信这是我应该在一般情况下为应用程序中的所有按钮组件添加跟踪事件处理的路径。我也一直在阅读关于停止的各种堆栈溢出答案,然后重新发布,本地事件,如submit
(应用程序的各个部分使用由Formik捕获,例如,因此为什么简单的点击处理是不够的)。这三个答案对我现在的处境很有帮助:
如何在门户包装器中有条件地调用React useRef并获得更好的代码?
如何在React和Typescript中使用ref
点击跟踪的高阶反应组件
在这一点上,我得到一个关于不能在event
对象上调用dispatchEvent
的错误,因为显然SyntheticEvent
类型不能传递给dispatchEvent
,我担心我在这条路径上走错了叉。
简而言之,对于"不可见",什么是正确的,或者可能是反应推荐的方式?包装一个组件,操作它的函数和事件处理程序,然后在不停止它们的情况下继续调用所有这些函数和事件处理程序?在本例中,使用实现跟踪库的特定用例。
我搜索了react文档,也搜索了一些不同的教程,并浏览了各种跟踪库和react集成的代码库(如Segment和Google Analytics),但没有找到这些方面的例子,这让我担心我在一个未被涉足的,因此不正确的道路上,并有一个根本的误解。
不需要为您的用例处理内部ref
,只需转发它。
只要确保用您自己的实现包装onClick
事件处理程序,首先调用您的analyticsTrack
函数:
const CustomButton = forwardRef<any, CustomButtonProps>(
({ trackingName, children, onClick, ...rest }, ref) => {
function handleClick(event: any) {
analyticsTrack("Button Clicked", {
props: rest,
ariaLabel: rest["aria-label"],
trackingName,
buttonText: typeof children === "string" ? children : ""
});
if (onClick) {
onClick(event);
}
}
return (
<Button {...rest} onClick={handleClick} ref={ref}>
{children}
</Button>
);
}
);
演示:https://codesandbox.io/s/nameless-frog-jtdves?file=/src/App.tsx