如何在React onClick中增删同一事件监听器避免无限堆叠
解决方案
核心逻辑是通过持久化存储每个目标SVG元素对应的回调函数引用,保证addEventListener和removeEventListener操作的是同一个函数,解决监听堆叠问题。这里选用WeakMap做存储载体,以DOM元素为键,不会阻止DOM元素的垃圾回收,避免内存泄漏。
修改后完整代码:
// 定义在组件顶层作用域,不要放在barClick函数内部 const callbackMap = new WeakMap<SVGRectElement, () => void>(); const barClick = (event) => { const target = event.target as SVGRectElement; const parent = target.parentElement; if (!parent) return; // 先移除当前元素之前绑定的旧监听,避免堆叠 const oldChangeBack = callbackMap.get(target); if (oldChangeBack) { parent.removeEventListener('click', oldChangeBack); callbackMap.delete(target); } // 修改当前选中柱形的颜色 target.style.fill = cvar('colorSecondary'); // 定义新的回调并存入WeakMap,保证后续可以拿到相同引用移除 const changeBack = () => { target.style.fill = cvar('colorPrimary'); // 回调执行后自动清理监听和存储记录 parent.removeEventListener('click', changeBack); callbackMap.delete(target); console.log('changed'); }; callbackMap.set(target, changeBack); // 绑定新的父级点击监听 parent.addEventListener('click', changeBack); };
核心要点
- 所有回调统一存在组件全局的
callbackMap中,同一个元素的回调可以被精准找到并移除 - 每次触发barClick时先清理当前元素之前绑定的旧回调,避免多次点击生成多个监听
- changeBack执行时会自动清理自身的监听和存储记录,避免无效数据残留
- 若组件存在卸载场景,可在useEffect的清理函数中遍历已绑定的回调做移除操作,进一步规避内存泄漏风险
内容的提问来源于stack exchange,提问作者Speckope
相关产品推荐
相关产品推荐

