ReactJs结合useEffect使用时接收消息重复显示两次问题求助
React + Socket.IO 即时通讯单条消息重复显示问题修复
问题根因
接收端单条消息重复渲染的核心问题出在useEffect的事件绑定逻辑上,共有两个触发点:
- 未清理socket事件监听器导致重复绑定:当前
useEffect仅在依赖(socket实例)变化时新增receive_message事件监听,但没有在effect销毁阶段移除旧的监听器。一旦组件重渲染、socket引用变动触发effect重执行,就会叠加绑定多个相同的事件回调,同一条服务端消息推送过来时,会触发多次setMessageList操作,把同一条消息多次追加到列表里。如果你使用React 18严格模式,开发环境下组件会默认执行两次挂载/卸载流程,没有清理逻辑的话首次加载就会直接绑定两个监听器,第一次收消息就会出现重复渲染的问题。 - (额外逻辑隐患)如果你的服务端广播逻辑是向房间内所有连接(包括消息发送者本人)推送消息,发送端在调用
socket.emit时已经手动把消息插入了一次列表,后续收到自己发的消息广播时会再插入一次,发送端本身也会出现重复消息问题。
修复方案
- 为
useEffect添加清理函数,每次effect重跑、组件卸载时移除已绑定的事件监听器,杜绝监听器重复注册。 - (推荐)移除发送消息时手动更新消息列表的逻辑,所有端(包括发送方)统一在收到
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
相关产品推荐
相关产品推荐

