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

React中Socket.io客户端多次建立连接而非单次的问题排查

Socket.io 多次连接问题排查

我正在学习Socket基础用法,搭建Socket.io连接时发现客户端会多次尝试连接才稳定。客户端React应用刷新一次后,服务器控制台日志如下:

[0] Client connected: AkHpPfA3ZjXoAPkQAAAB
[0] Client connected: PLRhfqFKVlBnNvq2AAAD
[0] CNFish has joined the chat
[0] Client connected: oX6iyoNsMpf2kLOzAAAF
[0] Client connected: ddV5y6UTczq0DUw4AAAH

我会检查是否有已登录用户,存在则建立Socket连接并发送setup事件,返回“用户名已加入聊天”提示,但日志显示之后出现多次连接尝试。作为Socket新手,我认为应该只建立一次连接,想请教这是React重渲染导致的特性,还是连接配置有误?


服务器端代码

const io = new Server(server, { pingTimeout: 5000 });
io.on("connection", (socket) => {
  console.log(`Client connected: ${socket.id}`);
  socket.on("setup", (userData) => {
    console.log(`${userData.username} has joined the chat`.green);
    socket.join(userData._id);
    socket.emit("connected");
  });
});

客户端App.js代码

const { user } = useSelector((state) => state.auth);
const [socket, setSocket] = useState(null);
const [socketConnected, setSocketConnected] = useState(false);
console.log(socketConnected);
if (user) {
  setAuthToken(user.token);
}
useEffect(() => {
  if (!socket) {
    setSocket(io("/"));
  }
}, [socket]);
// subscribe to the socket event
useEffect(() => {
  if (!socket) return;
  socket.emit("setup", user);
  socket.on("connected", () => {
    setSocketConnected(true);
  });
  socket.on("disconnect", () => {
    setSocketConnected(false);
    setSocket(null);
  });
  // close the socket connection when the component unmounts
  return () => {
    socket.close();
  };
}, [socket]);

问题原因

你的代码存在几个关键问题,直接导致多次连接:

  1. 第一个useEffect依赖错误:将socket作为依赖,当socket从null变为实例后会重复执行;断开时设置socket为null,又会触发该useEffect重新创建连接,形成循环。
  2. 渲染阶段调用状态更新:直接在组件渲染逻辑中调用setAuthToken,违反React规则,可能引发无限重渲染。
  3. 依赖缺失:第二个useEffect仅依赖socket,但user变化时无法触发重新发送setup事件,且初始user未就绪时可能发送无效数据。

修复方案

调整客户端代码,确保连接逻辑稳定且符合React规范:

const { user } = useSelector((state) => state.auth);
const [socket, setSocket] = useState(null);
const [socketConnected, setSocketConnected] = useState(false);

// 仅在用户存在且未创建连接时初始化socket
useEffect(() => {
  if (user && !socket) {
    const newSocket = io("/");
    setSocket(newSocket);

    newSocket.on("connected", () => {
      setSocketConnected(true);
    });
    newSocket.on("disconnect", () => {
      setSocketConnected(false);
    });

    // 组件卸载时关闭连接
    return () => {
      newSocket.close();
    };
  }
}, [user, socket]);

// 确保socket和user都就绪时发送setup事件
useEffect(() => {
  if (socket && user) {
    socket.emit("setup", user);
  }
}, [socket, user]);

// 将token设置逻辑放到useEffect中,避免渲染阶段执行
useEffect(() => {
  if (user) {
    setAuthToken(user.token);
  }
}, [user]);

关键改动说明

  • 合并连接初始化逻辑到单个useEffect,依赖user和socket,避免重复创建连接。
  • 断开连接时不再重置socket为null,打破循环创建的触发条件。
  • 将setAuthToken移至单独的useEffect,遵循React状态更新规则。
  • 拆分setup事件发送逻辑,确保数据有效性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 11:36:06