ReactJS中EventListener与闭包导致事件回调多次打印如何解决
问题根源
梳理两个核心错误点:
addOnEvent函数绑定事件时,实际注册到document的是新生成的匿名箭头函数(e)=>callback(e.detail),而你调用removeEvent时传入的是原始的customFunction,两个函数引用完全不一致,导致事件移除逻辑完全失效- 组件每次渲染/
some_state更新时,都会生成新的customFunction函数引用,加上useEffect依赖some_state,每次更新都会重复注册新的事件监听,旧的监听又没被移除,自然会触发多次日志打印
修复方案
方案1:修正事件绑定/移除的工具函数
先把事件注册和移除的逻辑对齐,保证移除的是实际绑定的函数:
// 修改addOnEvent,返回实际绑定的函数,供移除时使用 function addOnEvent(event_name, callback){ const handler = (e) => callback(e.detail); document.addEventListener(event_name, handler); return handler; } function removeEvent(event_name, handler){ document.removeEventListener(event_name, handler); }
然后修改组件逻辑,同时用useCallback稳定customFunction的引用:
import React, { useEffect, useState, useCallback } from "react"; // 上述修改后的工具函数 function App() { const [some_state, set_some_state] = useState([]); // 用useCallback包裹,依赖不变的情况下函数引用不会更新 const customFunction = useCallback((e) => { // 此处可正常执行更新some_state的逻辑 console.log("Console prints multiple times") }, []); // 此处填写customFunction依赖的变量,React框架 useState 返回的setter函数引用天生稳定,不需要添加 useEffect(() => { const handler = addOnEvent('custom_event', customFunction) return () => { removeEvent('custom_event', handler) }; }, [customFunction]); // 依赖改为稳定的customFunction,不需要监听some_state除非业务逻辑需要 return ( <div className="App"> </div> ); } export default App;
方案2:简化工具函数逻辑
如果不需要统一封装e.detail的处理,也可以直接让注册、移除的是同一个函数:
function addOnEvent(event_name, callback){ document.addEventListener(event_name, callback); } function removeEvent(event_name, callback){ document.removeEventListener(event_name, callback); } // 事件触发时自行取detail即可 const customFunction = useCallback((e) => { const data = e.detail; console.log("Console prints multiple times") }, []);
补充说明
如果customFunction内部需要用到最新的some_state,可以选择两种处理方式:
- 把
some_state加到useCallback的依赖数组里,useCallback会自动更新函数引用,useEffect会自动移除旧监听、注册新监听,不会出现重复绑定 - 用
useRef存储some_state的最新值,避免频繁更新依赖,适合状态更新频率高的场景
内容的提问来源于stack exchange,提问作者Ashish C
相关产品推荐
相关产品推荐

