使用store中全局Socket.IO实例时socket.on事件被重复触发如何解决?
问题原因
- 全局Socket实例是单例对象,生命周期和应用一致,不会随组件卸载/重渲染销毁
- 你将
socket.on('finding', ...)直接写在组件顶层执行,组件每触发一次重渲染,就会给同一个Socket实例新增一个finding事件的匿名监听器,旧的监听器不会自动清除 - 后端触发一次
finding事件时,Socket实例上绑定的所有同事件名监听器都会依次执行,所以打印次数等于累计注册的监听器数量,组件重渲染次数越多打印次数越高 - 组件内创建Socket实例时,每次组件重渲染/卸载都会销毁旧实例、创建新实例,旧实例上绑定的监听器会被回收,所以只会打印一次
解决方案
核心逻辑是:组件绑定的Socket事件监听器,必须在组件卸载时主动清理,正确实现方式如下:
- 将事件注册逻辑放到
useEffect中,同时返回清理函数调用socket.off移除对应监听器,注意要给监听器使用命名函数,保证off时能匹配到相同的函数引用:
const socketStore = useContext(SocketStoreContext); const socket = socketStore.socket; // 此处替换为你实际获取user对象的逻辑 const user = useUser() const handleFinding = useCallback(() => { console.log('called'); }, []) useEffect(() => { // 组件挂载时注册监听器 socket.on('finding', handleFinding); // 组件卸载时清理监听器 return () => { socket.off('finding', handleFinding); } }, [socket, handleFinding]) const handleFindPartner = () => { socket.emit('find', { token: user.token, }); };
- 如果项目有多个组件都需要监听同一个Socket事件,可以统一在Socket Store层做事件分发,避免各个组件重复注册/清理的逻辑,减少冗余代码。
内容的提问来源于stack exchange,提问作者Ted Vu
相关产品推荐
相关产品推荐

