React Hooks集成WebSocket出现双连接、消息重复广播问题排查
React WebSocket 重复连接、重复收消息问题修复
问题根因
问题本质是副作用逻辑不符合 React useEffect 的执行规则,叠加 React 18 开发环境严格模式的校验逻辑导致:
- React 18 开发环境默认开启严格模式,组件首次挂载时会执行「挂载→卸载→二次挂载」的流程,用于检测未正确清理的副作用。你的连接创建逻辑没有写卸载时的清理回调,第一次挂载创建的 WebSocket 连接不会被关闭,二次挂载时又新建一个连接,最终同时存在2个有效连接。
- 你把连接创建和
onmessage事件绑定拆成了两个独立的useEffect:绑定事件的effect依赖为空数组,仅在首次挂载时执行一次,如果连接实例因为重渲染被重新创建,新实例的onmessage回调不会被绑定,旧实例的回调依然存在,会进一步导致消息重复接收。 - 第一个effect的依赖项包含整个
location对象,react-router 返回的 location 对象会在路由状态发生任意细微变化时生成新引用,即使房间号参数没有变化,也可能意外触发effect重执行,创建多余连接。
修复方案
将连接创建、事件绑定、销毁逻辑合并到同一个 useEffect 中,补充清理回调,精简依赖项:
useEffect(() => { if (!location.search) { navigate("/") return; } // 新建ws实例 const wsInstance = new W3CWebSocket(`ws://127.0.0.1:8000/ws/chat/${room}/`) client.current = wsInstance // 统一绑定所有事件 wsInstance.onopen = () => console.log("ws opened"); wsInstance.onclose = () => console.log("ws closed"); wsInstance.onmessage = (message) => { const data = JSON.parse(message.data); setMessages(prev => [ ...prev, { msg: data.message, name: data.name, } ]) } // 副作用清理函数:组件卸载/effect重执行前关闭旧连接 return () => { wsInstance.close() if (client.current === wsInstance) { client.current = null } } // 仅保留实际依赖的变量,移除整个location对象避免无意义重连 }, [location.search, navigate, room])
修复后开发环境下严格模式触发双执行时,第一次创建的连接会被清理函数主动关闭,最终只会保留1个有效连接,发送消息也不会再被重复广播。
问题现象参考

内容的提问来源于stack exchange,提问作者Hossein Gholami
相关产品推荐
相关产品推荐

