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

ReactJs结合useEffect使用时接收消息重复显示两次问题求助

React + Socket.IO 即时通讯单条消息重复显示问题修复

问题根因

接收端单条消息重复渲染的核心问题出在useEffect的事件绑定逻辑上,共有两个触发点:

  • 未清理socket事件监听器导致重复绑定:当前useEffect仅在依赖(socket实例)变化时新增receive_message事件监听,但没有在effect销毁阶段移除旧的监听器。一旦组件重渲染、socket引用变动触发effect重执行,就会叠加绑定多个相同的事件回调,同一条服务端消息推送过来时,会触发多次setMessageList操作,把同一条消息多次追加到列表里。如果你使用React 18严格模式,开发环境下组件会默认执行两次挂载/卸载流程,没有清理逻辑的话首次加载就会直接绑定两个监听器,第一次收消息就会出现重复渲染的问题。
  • (额外逻辑隐患)如果你的服务端广播逻辑是向房间内所有连接(包括消息发送者本人)推送消息,发送端在调用socket.emit时已经手动把消息插入了一次列表,后续收到自己发的消息广播时会再插入一次,发送端本身也会出现重复消息问题。

修复方案

  1. 为useEffect添加清理函数,每次effect重跑、组件卸载时移除已绑定的事件监听器,杜绝监听器重复注册。
  2. (推荐)移除发送消息时手动更新消息列表的逻辑,所有端(包括发送方)统一在收到receive_message事件时再更新列表,保证消息追加的入口唯一,从逻辑上避免重复插入。

修复后代码

const sendMessage = async () => {
  if (currentMessage !== "") {
    const messageData = {
      room: room,
      author: username,
      message: currentMessage,
      time:
        new Date(Date.now()).getHours() +
        ":" +
        new Date(Date.now()).getMinutes(),
    };

    await socket.emit("send_message", messageData);
    // 移除手动追加消息的逻辑,统一由receive_message事件触发更新
    setCurrentMessage("");
  }
};

useEffect(() => {
  const handleReceiveMessage = (data) => {
    setMessageList((list) => [...list, data]);
  };
  // 绑定事件监听
  socket.on("receive_message", handleReceiveMessage);
  
  // effect清理函数:移除旧的监听器,避免重复绑定
  return () => {
    socket.off("receive_message", handleReceiveMessage);
  };
}, [socket]);

如果暂时不想调整服务端广播逻辑、保留发送端手动插入消息的逻辑,只需要加上useEffect的清理函数即可解决接收端重复显示的问题,但长期来看统一消息更新入口的方案更易维护,也能避免其他边界case。

内容的提问来源于stack exchange,提问作者Kaushik Sharma

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.02 05:21:46