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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 16:06:05