React中如何为同一元素同时实现短按和长按触发不同回调
长短按区分实现方案
原有代码问题排查
- 所有事件回调都固定将
startLongPress设为true、startShortPress设为false,完全没有短按逻辑的触发入口,所以短按永远不会执行 startLongPress状态一旦被设为true就没有重置为false的逻辑,useEffect会因为状态变化反复触发长按定时器,就会出现未按压也周期性触发长按的异常- 用state存储计时器触发标记会带来不必要的组件重渲染,性能较差
修正后的Hook实现
import { useRef, useCallback } from "react"; export default function useLongAndShortPress( longPressCallback: () => void = () => console.log("Long press callback"), shortPressCallback: () => void = () => console.log("Short press callback"), ms = 1000 ) { // 存储长按定时器ID const timerRef = useRef<NodeJS.Timeout | null>(null); // 标记长按回调是否已经触发 const longPressTriggeredRef = useRef(false); // 处理按压开始逻辑 const handlePressStart = useCallback(() => { // 清空历史定时器避免重复触发 if (timerRef.current) clearTimeout(timerRef.current); longPressTriggeredRef.current = false; // 开启长按计时 timerRef.current = setTimeout(() => { longPressCallback(); longPressTriggeredRef.current = true; }, ms); }, [longPressCallback, ms]); // 处理按压结束/取消逻辑 const handlePressEnd = useCallback(() => { if (timerRef.current) { clearTimeout(timerRef.current); timerRef.current = null; } // 仅当长按未触发时执行短按回调 if (!longPressTriggeredRef.current) { shortPressCallback(); } }, [shortPressCallback]); return { onMouseDown: handlePressStart, onTouchStart: handlePressStart, onMouseUp: handlePressEnd, onMouseLeave: handlePressEnd, onTouchEnd: handlePressEnd, onTouchCancel: handlePressEnd, }; }
使用示例
使用方式和原有逻辑完全兼容,无需修改业务代码:
import useLongAndShortPress from './useLongAndShortPress'; function MyComponent (props) { const longAndShortPress = useLongAndShortPress( () => alert("LONG PRESS"), () => alert("SHORT PRESS"), 1000 ); return ( <Page> <Button {...longAndShortPress}> Click me </Button> </Page> ); };
方案优势
- 内部状态用ref存储,不会触发组件额外重渲染,性能更优
- 逻辑完全符合需求:按压时长不足阈值触发短按,达到阈值触发长按,长按触发后不再执行短按
- 所有场景下都会自动清理定时器,不会出现内存泄漏或异常触发的问题
内容的提问来源于stack exchange,提问作者Tmfwang
相关产品推荐
相关产品推荐

