You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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个有效连接,发送消息也不会再被重复广播。

问题现象参考

WebSocket重复连接控制台截图

内容的提问来源于stack exchange,提问作者Hossein Gholami

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 07:06:23