React中useEffect触发Socket IO事件两次问题求助
解决Socket IO事件在useEffect中触发两次的问题
问题根源分析
你的代码里有两个核心问题导致事件重复触发:
- useEffect依赖数组冗余:依赖里包含了
players和setPlayer,players每次更新都会触发useEffect重新执行,重新创建Socket连接并绑定事件,旧连接没断开的话就会存在两个活跃连接,同一事件被两次监听。 - Socket连接未彻底清理:之前的清理函数只移除了事件监听,但没有断开旧的Socket连接,旧连接依然会接收服务端广播的事件。
分步解决方案
1. 修正useEffect的依赖数组与清理逻辑
setPlayers是React useState生成的稳定函数,不需要加入依赖;players绝对不能放在依赖里,否则每次玩家列表更新都会重建连接。只保留gameID作为依赖(仅当游戏ID变化时才需要重新连接),同时在清理函数中断开旧连接:
const socket = useRef(); useEffect(() => { // 创建Socket连接 socket.current = io(SOCKET_URL, { query: { gameID: gameID }, }); // 抽离事件处理函数,方便后续精准移除监听 const handlePlayerJoin = (player) => { console.log("new player:", player); setPlayers(prevPlayers => [...prevPlayers, player]); }; const handlePlayerLeave = (player) => { console.log("user left:", player); setPlayers(prevPlayers => prevPlayers.filter(p => p.username !== player.username)); }; socket.current.on("join-game", handlePlayerJoin); socket.current.on("leave-game", handlePlayerLeave); // 清理函数:断开连接+移除监听 return () => { socket.current.off("join-game", handlePlayerJoin); socket.current.off("leave-game", handlePlayerLeave); socket.current.disconnect(); // 彻底断开旧连接,避免残留 }; }, [gameID]); // 仅保留gameID作为依赖项
2. 移除客户端joiningGame中的重复setPlayers
你在joiningGame里既调用了socket.emit("join-game"),又手动执行setPlayers,而服务端收到后会广播该事件,客户端监听后又会执行一次setPlayers,导致玩家被重复添加。删掉手动的setPlayers:
const joiningGame = async (user) => { await fetchGame(); const player = new Player(user.username, game.current.startingHealth, 0, 0); setPlayer(player); const { data } = await joinGame({ gameID: gameID, player: player }); if (data) { socket.current.emit("join-game", player); // 移除下面这句,交给Socket事件统一处理 // setPlayers((players) => [...players, player]); } };
3. 服务端确认广播目标准确性
确保服务端使用当前连接对应的游戏ID进行广播,避免错误广播到其他房间:
socket.on("join-game", (player) => { console.log(player, 'is joining'); // 从连接的query中获取当前游戏ID,确保广播目标正确 const targetGameID = socket.handshake.query.gameID; io.to(targetGameID).emit("join-game", player); });
额外验证点
- 打开浏览器控制台的Network标签,查看WebSocket连接数,确认每次切换
gameID时旧连接会被断开,仅存在一个活跃连接 - 检查组件是否存在重复渲染的情况(比如被多次挂载),避免重复初始化Socket
内容的提问来源于stack exchange,提问作者JerryC
相关产品推荐
相关产品推荐

