Socket.io与React实现的在线用户列表登出更新异常问题
问题根因
核心错误出在后端用户登出时的数组删除逻辑与存储结构不匹配,同时前端存在事件监听的隐性bug:
- 数组删除逻辑错误:
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,刚好删对人,属于巧合性正常
- 前端事件监听不规范:监听
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
相关产品推荐
相关产品推荐

