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

React中socket.io消息重复收发、broadcast.emit不生效问题

React 集成 socket.io 消息重复收发、广播不生效问题修复

问题现象

React项目中使用socket.io时,消息会重复发送/接收2到4次,初步判断和组件重复渲染相关。已尝试将socket初始化init函数从container.jsx移动到app.jsx、清理浏览器缓存,两种方案均未解决问题。控制台日志显示ChatList组件存在重复渲染情况,同时后端使用socket.broadcast.emit推送消息时存在不生效问题。

控制台日志截图

问题相关代码

客户端socket封装

import { useEffect } from "react";
import io from "socket.io-client";

let socket;

export const init = () => {
    console.log("Connecting...");
    socket = io.connect("http://localhost:3000", {
        transports: ["websocket"],
    });
    socket.on("connect", () => console.log("Connected!"));
};

export const sendMessage = (message) => {
    if (socket) {
        socket.emit("new-message", message);
    }
};

export const onNewMessage = (callback) => {
    if (!socket) return;
    socket.on("receive-message", (message) => {
        callback(message);
        console.log("Received message: ", message);
    });
};

服务端代码

const app = require("express")();
const http = require("http").Server(app);
const io = require("socket.io")(http);
const cors = require("cors");

const Messages = require("./lib/Messages");

app.use(cors());

app.get("/", (req, res) => {
    res.end("Merhaba Socket.IO");
});

io.on("connection", (socket) => {
    console.log("a user connected");

    Messages.list((data) => {
        console.log(data);
        socket.emit("message-list", data);
    });

    socket.on("new-message", (message) => {
        console.log(message);
        Messages.upsert({ message });

        socket.broadcast.emit("receive-message", message);
    });

    socket.on("disconnect", () => console.log("a user disconnected"));
});

http.listen(process.env.PORT || "3000", () => {
    console.log("listening on *:3000");
});

ChatList.jsx 组件代码

import { useChat } from "../context/ChatContext";
import styles from "./style.module.css";
import ChatItem from "./ChatItem";

export default function ChatList() {
    const { messages } = useChat();
    console.log(messages);
    return (
        <div className={styles.chatlist}>
            <div>
                {messages.map((item, key) => (
                    console.log("Message: ",item),
                    <ChatItem key={key} value={item} />
                ))}
            </div>
        </div>
    );
}

根因分析

  • 消息重复收发核心原因:两点问题共同导致
    • socket初始化没有做单例校验,React重渲染、useEffect重复执行时会创建多个socket连接,同一个客户端和后端建立了多条通信链路
    • 事件监听没有做解绑逻辑,每次调用onNewMessage都会给socket实例新增一个receive-message事件回调,消息到达时所有绑定过的回调会依次执行,直接导致同一条消息被处理多次
  • socket.broadcast.emit不生效原因:对API逻辑理解有误,该方法只会向除当前发送消息的socket之外的所有连接推送消息,发送消息的用户本人不会收到这条消息,业务要求全员可见时就会出现“发送方收不到消息”的异常表现。

修复方案

  1. 补全socket单例校验,避免重复创建连接
    修改init方法,初始化前先判断socket实例是否已存在且处于连接状态,已初始化则直接返回:
export const init = () => {
    // 新增单例判断,已连接则不重复创建
    if (socket?.connected) return;
    console.log("Connecting...");
    socket = io.connect("http://localhost:3000", {
        transports: ["websocket"],
    });
    socket.on("connect", () => console.log("Connected!"));
};
  1. 增加事件监听清理逻辑,避免重复绑定
    修改onNewMessage方法,绑定新监听前先移除旧的同事件监听,同时新增断开连接的清理方法,供组件卸载时调用:
export const onNewMessage = (callback) => {
    if (!socket) return;
    // 绑定新回调前先移除旧的同事件监听
    socket.off("receive-message");
    socket.on("receive-message", (message) => {
        callback(message);
        console.log("Received message: ", message);
    });
};

// 新增清理方法
export const disconnectSocket = () => {
    if (socket) {
        socket.off("receive-message");
        socket.disconnect();
        socket = null;
    }
}
  1. 修正后端广播逻辑
    如果需要发送方也能收到自己发的消息,将socket.broadcast.emit替换为io.emit(向所有连接全量广播),或者在广播后单独给当前socket推送一次:
socket.on("new-message", (message) => {
    console.log(message);
    Messages.upsert({ message });
    // 全量广播,所有连接(包括发送方)都能收到消息
    io.emit("receive-message", message);
});
  1. 规范React侧生命周期调用
    将socket初始化、监听绑定的逻辑放在useEffect中,依赖项设为空数组保证仅在组件挂载时执行一次,return的清理函数中调用断开连接的方法,避免组件重渲染时重复执行逻辑:
// 业务组件中使用示例
useEffect(() => {
    init();
    onNewMessage((msg) => {
        // 写入消息列表更新逻辑
    });
    return () => {
        disconnectSocket();
    };
}, []);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 16:33:41