React中Socket重复接收消息导致数组内容多次渲染问题排查
问题原因
- 最核心的原因是Socket事件监听器重复注册:你的
socket.on监听代码直接写在组件函数体内部,每次组件触发重渲染时,都会新增一个完全相同的room事件监听器。当后端推送1次消息时,多个重复的监听器会同时触发回调,多次执行setMessages添加同一条消息,最终导致数组内出现多条重复内容。 - React 18 严格模式加剧了这个问题:开发环境下严格模式会执行两次组件挂载逻辑,如果没有清理监听器的逻辑,会直接多注册一倍的监听器。
- 额外排查点:确认后端
io.emit(id, sm)中的id参数和前端监听的room参数完全一致,避免错位监听导致收到多轮消息。
解决方案
1. 用useEffect包裹Socket监听逻辑,添加销毁回调
把Socket监听逻辑放到useEffect中,同时在返回的清理函数中调用socket.off移除对应事件的监听器,确保每个事件只会注册一次:
import { useEffect, useState } from 'react'; const [messages, setMessages] = useState([]); useEffect(() => { // 定义事件回调函数 const handleReceiveMsg = (msg) => { setMessages(prev => [...prev, msg]); }; // 注册监听器 socket.on(room, handleReceiveMsg); // 组件卸载/依赖更新时移除监听器 return () => { socket.off(room, handleReceiveMsg); }; // 依赖项要加入room、socket实例,确保变量变化时重新注册正确的监听器 }, [room, socket]);
2. 可选:添加消息去重兜底逻辑
如果担心后续还会出现重复推送的情况,可以给每条消息添加唯一标识(后端生成消息ID),添加消息前先判断是否已经存在相同ID的消息,避免重复添加:
// 假设后端返回的msg对象带有唯一id字段msgId const handleReceiveMsg = (msg) => { setMessages(prev => { const isExist = prev.some(item => item.msgId === msg.msgId); if (isExist) return prev; return [...prev, msg]; }); };
3. 可选:优化渲染key
当前你用数组下标index作为渲染的key,当数组元素变化时可能导致渲染异常,建议替换为消息的唯一ID:
messages.map((message) => ( <div className="message " key={message.msgId}> <p className="meta"> {message.user} <span> {message.time} </span> <span></span></p> <p className="text" style={{ "maxWidth": "80ch", "overflowWrap": "break-word" }}> {message.msg} </p> </div> ))
内容的提问来源于stack exchange,提问作者VAISHAKH_GK
相关产品推荐
相关产品推荐

