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

Socket.io与React实现的在线用户列表登出更新异常问题

问题根因

核心错误出在后端用户登出时的数组删除逻辑与存储结构不匹配,同时前端存在事件监听的隐性bug:

  1. 数组删除逻辑错误:onlineUsers存储的每个元素是{user: 用户名, id: socket.id}格式的对象,但删除时直接传入纯用户名字符串调用onlineUsers.indexOf(user)。indexOf做严格相等匹配,对象不可能和字符串相等,因此永远返回-1。而Array.splice(-1, 1)的语义是删除数组最后一个元素,刚好完美复现你观测到的所有现象:
    • 按用户1、用户2顺序登录时,用户1在数组索引0位置,用户2在索引1(数组最后一位)
    • 用户1先登出:splice收到索引-1,删除最后一位的用户2,用户1残留在数组中,和描述的异常完全一致
    • 用户2先登出:splice收到索引-1,同样删除最后一位的用户2,刚好删对人,属于巧合性正常
  2. 前端事件监听不规范:监听online_users事件的useEffect未传入依赖数组,也未做事件解绑,会导致组件每次重渲染都重复注册监听,后续可能引发列表重复渲染、状态更新混乱的问题。
修复方案

后端代码修复

将user_disconnected事件中错误的索引查找逻辑,替换为按对象属性匹配的正确逻辑:

socket.on("user_disconnected", (user) => {
  console.log(user);
  const userExists = checkUserArray(user, onlineUsers);
  console.log(userExists);
  if (userExists) {
    // 匹配user字段等于当前登出用户名的元素索引
    const targetIndex = onlineUsers.findIndex(item => item.user === user);
    // 索引合法时再执行删除
    if (targetIndex > -1) {
      onlineUsers.splice(targetIndex, 1);
    }
  }
  socket.emit("online_users", onlineUsers);
  socket.in("public_chat").emit("online_users", onlineUsers);
  console.log(onlineUsers);
  console.log(`${user} is now offline.`);
});

可选安全优化:不建议依赖前端传入的用户名判定登出用户,可直接利用socket实例自带的disconnect事件,通过存储的socket.id匹配删除对应用户,避免恶意请求伪造登出操作踢掉其他在线用户,参考实现:

io.on("connection", (socket) => {
  // 原有connection逻辑保留
  // 新增原生断开事件监听,无需前端触发
  socket.on("disconnect", () => {
    const targetIndex = onlineUsers.findIndex(item => item.id === socket.id);
    if (targetIndex > -1) {
      const leaveUser = onlineUsers[targetIndex].user;
      onlineUsers.splice(targetIndex, 1);
      io.to("public_chat").emit("online_users", onlineUsers);
      console.log(`${leaveUser} is now offline.`);
    }
  })
})

前端代码修复

给socket事件监听的useEffect添加空依赖数组,同时在清理函数中解绑事件,避免重复注册监听:

useEffect(() => {
  const updateOnlineUsers = (onlineUsers) => {
    setUsers(onlineUsers);
  };
  socket.on("online_users", updateOnlineUsers);
  // 组件卸载时移除监听
  return () => {
    socket.off("online_users", updateOnlineUsers);
  };
}, []);
修复验证

修复后执行原复现流程:

  • 用户1登录:数组索引0存入用户1信息,列表正常展示
  • 用户2登录:数组索引1存入用户2信息,列表正常展示
  • 用户1先登出:匹配到索引0执行删除,列表仅保留用户2,无错误删除
  • 用户2先登出:匹配到索引1执行删除,列表仅保留用户1,符合预期
  • 所有用户登出后数组为空,无残留条目

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 14:00:54