React应用中mitt库事件监听器重复触发问题详解及解决方案
本文分析了在React应用中使用mitt库进行组件间通信时,事件监听器重复触发的常见问题,并提供有效的解决方案。
问题描述:
使用mitt库构建的事件发射器(emitter),组件A通过emitter.emit('e1', data)发射事件,组件B通过emitter.on('e1', callback)监听事件。然而,点击组件A的按钮后,组件B的回调函数callback被执行多次,而非预期的一次。
示例代码:
emitter/index.ts:
import type { emitter } from 'mitt' import mitt from 'mitt' export default mitt() as emitter<any>
组件A:
import { useEffect } from 'react' import emitter from '../../emitter' function Index() { useEffect(() => { console.log('组件A挂载') }, []) return ( <button onClick={() => { console.log('按钮被点击'); emitter.emit('e1', { "name": "zhangsan" }); }}>A发送信息到B</button> ); } export default Index;
组件B:
import { useEffect } from 'react' import emitter from '../../emitter' function Index() { useEffect(() => { console.log('组件B挂载'); emitter.on('e1', (e) => { console.log('mitt test', e); }); }, []); return <div>BBB</div>; } export default Index;
问题原因分析:
组件B在useEffect钩子函数中添加事件监听器,空依赖数组导致该函数在组件挂载后执行一次。关键问题在于:emitter.on('e1', callback)注册的监听器未在组件卸载时移除。每次组件B重新渲染(例如,由于其他状态变化),useEffect都会重新执行,再次注册相同的监听器,导致事件重复触发。
解决方案:
在useEffect的返回值中添加清理函数,在组件卸载前移除监听器:
import { useEffect } from 'react' import emitter from '../../emitter' function Index() { useEffect(() => { const unsubscribe = emitter.on('e1', (e) => { console.log('mitt test', e); }); return () => { unsubscribe(); // 组件卸载时移除监听器 }; }, []); return <div>BBB</div>; } export default Index;
修改后的代码在组件卸载时调用unsubscribe()函数,移除已注册的事件监听器,有效避免重复触发。 这确保了每个组件生命周期内只注册一次监听器,解决了重复触发的问题。