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

Socket.IO多次重复触发连接事件问题排查求助

问题解决:Socket.IO .on() 增量式多次触发

问题根源

你的客户端代码中,newSocket.on("privateMessage") 和 newSocket.on("connectedClients") 直接写在ChatRoom组件函数内部,每次组件渲染(比如登录状态变化、状态更新触发重渲染)都会重复注册事件监听器。加上newSocket是组件外部的全局实例,即使组件卸载再挂载,之前绑定的监听器不会自动清除,最终导致事件触发次数持续累加。

修复方案

1. 用useEffect包裹事件监听逻辑,添加清理函数

将Socket事件的注册逻辑放到useEffect中,确保只在组件挂载时绑定一次监听器,组件卸载时自动移除,彻底避免重复绑定。

修改后的客户端代码:

const newSocket = io("http://localhost:3000", { autoConnect: false });
const ChatRoom = () => {
  const signedInUser = useSelector((state) => state.user.user);
  const isLoggedIn = useSelector((state) => state.user.isLoggedIn);

  const [selectedChat, setSelectedChat] = useState([]);
  const listItemClickHandler = (elem) => {
    setSelectedChat(elem);
  };

  // 处理Socket连接/断开逻辑,添加依赖确保状态变化时触发
  useEffect(() => {
    if (isLoggedIn) {
      newSocket.connect();
      newSocket.emit("setUsername", signedInUser);
    }
    return () => {
      newSocket.disconnect();
    };
  }, [isLoggedIn, signedInUser]);

  const [userList, setUserList] = useState([]);
  const [activeUsers, setActiveUsers] = useState([]);

  useEffect(() => {
    setActiveUsers(() =>
      userList.filter((elem) => elem[0] !== newSocket.id)
    );
  }, [userList, newSocket.id]);

  // 统一管理Socket事件监听与清理
  useEffect(() => {
    // 定义事件处理函数
    const handlePrivateMessage = (eventData) => {
      console.log("收到新消息");
      console.log("消息内容: " + eventData);
    };

    const handleConnectedClients = (users) => {
      if (users) {
        console.log("已连接客户端更新");
        setUserList(users);
      }
    };

    // 注册监听器
    newSocket.on("privateMessage", handlePrivateMessage);
    newSocket.on("connectedClients", handleConnectedClients);

    // 组件卸载时移除监听器
    return () => {
      newSocket.off("privateMessage", handlePrivateMessage);
      newSocket.off("connectedClients", handleConnectedClients);
    };
  }, []); // 空依赖确保只注册一次

  return (
    <div className="chatroom-container">
      <div className="chatroom-header">
        <h1>聊天室</h1>
      </div>
      <span>
        <ChatList
          listItemClick={(elem) => listItemClickHandler(elem)}
          activeUsers={activeUsers}
        />
        <div className="chat-area-conntainer">
          <ChatComponent selectedChat={selectedChat} socket={newSocket} />
        </div>
      </span>
    </div>
  );
};

const disconnectSocket = () => {
  if (newSocket) {
    newSocket.disconnect();
  }
};

2. 额外优化点

  • 第一个useEffect添加isLoggedIn和signedInUser依赖,确保登录状态或用户信息变化时,能重新处理Socket连接并发送最新用户名。
  • 第二个useEffect添加newSocket.id依赖,避免Socket未初始化时过滤用户列表出错。

修复原理

  • useEffect的空依赖数组保证事件监听器仅在组件首次挂载时注册一次。
  • 返回的清理函数通过socket.off()移除对应监听器,避免组件卸载后旧监听器残留。
  • 全局Socket实例不会因组件重复渲染被多次绑定监听器,从根源上阻止事件增量触发。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 13:46:08