React实现双击屏蔽单击:带参数场景的自定义Hook复用问题
解决带参数的单击/双击区分Hook复用问题
我需要实现一个定时器逻辑区分单击和双击,确保双击时不触发单击。现有自定义Hook useNoClickOnDblClick在无参数场景下正常工作,但遍历渲染MineCard卡片时,需要给每个卡片的单击/双击事件传递对应的level数据,直接把数据传入Hook会影响复用性,该怎么处理?
现有Hook代码
export const useNoClickOnDblClick = (onClick: () => void, onDoubleClick: () => void) => { const [clicks, setClicks] = useState(0); useEffect(() => { let singleClickTimer: string | number | NodeJS.Timeout | undefined; if (clicks === 1) { singleClickTimer = setTimeout(function () { onClick(); setClicks(0); }, 250); } else if (clicks === 2) { onDoubleClick(); setClicks(0); } return () => clearTimeout(singleClickTimer); }, [clicks]); return { clicks, setClicks }; };
基础无参数使用示例
const { setClicks } = useNoClickOnDblClick(handleRenderInfo, () => console.log('dbl click')); <SummaryCard data={data} onCardClick={() => setClicks((prev) => prev + 1)} />
待解决的传参场景
const { setClicks } = useNoClickOnDblClick(handleRenderLevelInfo, () => console.log('dbl click')); {mineData.levelArr.map((level, i) => { return ( <MineCard key={i} data={level} onCardClick={() => setClicks((prev) => prev + 1)} //onCardClick={() => handleRenderLevelInfo({ ...level, idx: i })} onCardDblClick={() => navigateTo({ path: `/${surveyPointsPrefix}/${mineID}/${level.level}` }) } /> ); })}
解决方案
1. 调整Hook,适配带参数的回调并封装事件逻辑
修改Hook,用泛型支持任意参数类型,同时返回统一的点击处理函数,既保留复用性,又能传递自定义参数:
export const useNoClickOnDblClick = <T>( onClick: (param: T) => void, onDoubleClick: (param: T) => void ) => { const [clicks, setClicks] = useState(0); const [clickParam, setClickParam] = useState<T | null>(null); useEffect(() => { let singleClickTimer: NodeJS.Timeout | undefined; if (clicks === 1 && clickParam !== null) { singleClickTimer = setTimeout(() => { onClick(clickParam); setClicks(0); setClickParam(null); }, 250); } else if (clicks === 2 && clickParam !== null) { onDoubleClick(clickParam); setClicks(0); setClickParam(null); } return () => clearTimeout(singleClickTimer); }, [clicks, clickParam, onClick, onDoubleClick]); const handleClick = (param: T) => { setClickParam(param); setClicks(prev => prev + 1); }; return { handleClick }; };
2. 给每个卡片分配独立的Hook实例
原来的写法会让所有卡片共用同一个clicks状态,导致点击不同卡片互相干扰,必须让每个卡片拥有独立的Hook状态,最规范的做法是封装子组件:
封装带Hook的子组件
const LevelCard = ({ level, idx, mineID, surveyPointsPrefix, handleRenderLevelInfo }) => { const { handleClick } = useNoClickOnDblClick( (param) => handleRenderLevelInfo(param), (param) => navigateTo({ path: `/${surveyPointsPrefix}/${mineID}/${param.level}` }) ); return ( <MineCard key={idx} data={level} onCardClick={() => handleClick({ ...level, idx })} /> ); };
父组件中遍历使用
{mineData.levelArr.map((level, i) => ( <LevelCard key={i} level={level} idx={i} mineID={mineID} surveyPointsPrefix={surveyPointsPrefix} handleRenderLevelInfo={handleRenderLevelInfo} /> ))}
3. 核心要点
- 独立状态隔离:每个卡片必须拥有自己的Hook实例,避免状态互相干扰。
- 泛型兼容任意参数:Hook通过泛型
<T>支持不同结构的参数,不影响在其他场景的复用。 - 简化外部调用:外部只需传入参数并调用
handleClick,无需关心内部定时器逻辑。
内容的提问来源于stack exchange,提问作者Tom
相关产品推荐
相关产品推荐

