React中使用useCallback后仍无法移除touchmove事件监听器的问题
React中使用useCallback后仍无法移除touchmove事件监听器的问题
这种情况我之前排查过好多次,明明调试时看到removeEventListener确实执行了,结果用getEventListeners(document)一看监听器还在,属实让人摸不着头脑😤。先看看你当前的代码:
const preventDefaultTouchMove = useCallback((event) => { event.preventDefault(); }, []); useEffect(() => { if (updatingLock) { document.addEventListener( 'touchmove', preventDefaultTouchMove, { passive: false } ); } else { document.removeEventListener( 'touchmove', preventDefaultTouchMove, { passive: false } ); } return () => { document.removeEventListener( 'touchmove', preventDefaultTouchMove, { passive: false } ); }; }, [updatingLock, preventDefaultTouchMove]);
咱们一步步拆解问题:你已经用useCallback稳住了事件处理函数的引用,这步完全没问题,但忽略了addEventListener和removeEventListener第三个参数的引用一致性——你每次调用时传递的{ passive: false }都是新创建的对象字面量,在部分浏览器中,不同的对象引用会被判定为不同的监听选项,导致移除操作无法匹配到之前添加的监听器。
给你两个关键的修复步骤:
- 第一步:统一监听选项的引用
把{ passive: false }提取成一个稳定的常量,保证添加和移除监听器时用的是同一个对象引用:
// 直接定义在组件外部,或者用useMemo缓存(如果是动态值的话) const TOUCH_EVENT_OPTIONS = { passive: false }; const preventDefaultTouchMove = useCallback((event) => { event.preventDefault(); }, []); useEffect(() => { if (updatingLock) { document.addEventListener( 'touchmove', preventDefaultTouchMove, TOUCH_EVENT_OPTIONS ); } else { document.removeEventListener( 'touchmove', preventDefaultTouchMove, TOUCH_EVENT_OPTIONS ); } return () => { document.removeEventListener( 'touchmove', preventDefaultTouchMove, TOUCH_EVENT_OPTIONS ); }; }, [updatingLock, preventDefaultTouchMove]);
- 第二步:优化Effect逻辑(可选,但更稳妥)
可以把逻辑简化成「先移除旧监听器,再根据状态决定是否添加新的」,避免因状态频繁切换导致的重复绑定问题:
const TOUCH_EVENT_OPTIONS = { passive: false }; const preventDefaultTouchMove = useCallback((event) => { event.preventDefault(); }, []); useEffect(() => { // 先移除可能存在的旧监听器,避免重复绑定 document.removeEventListener( 'touchmove', preventDefaultTouchMove, TOUCH_EVENT_OPTIONS ); if (updatingLock) { document.addEventListener( 'touchmove', preventDefaultTouchMove, TOUCH_EVENT_OPTIONS ); } return () => { document.removeEventListener( 'touchmove', preventDefaultTouchMove, TOUCH_EVENT_OPTIONS ); }; }, [updatingLock, preventDefaultTouchMove]);
最后再确认下updatingLock的状态更新是否正常——只要保证选项引用一致,再配合稳定的事件函数引用,监听器就能被正常移除了。
备注:内容来源于stack exchange,提问作者mule mule
相关产品推荐
相关产品推荐

