在 react 中为 socket.io 事件(如 `"game-found-status"`)动态添加监听器时,若未及时清理,会导致多次点击触发重复回调(如弹出多个 alert),根本原因是每次调用 `playerjoin()` 都新增一个监听器而未移除旧的。正确做法是使用 `useeffect` 声明式管理监听器生命周期,并在组件卸载时通过 `socket.off()` 清理。
问题核心在于:当前代码将 socket.on("game-found-status", ...) 直接写在 playerJoin() 函数体内——每次点击按钮都会注册一个新的事件监听器,但旧监听器从未被移除。即使 playerJoin() 本身只执行一次,Socket.IO 内部已累积多个同名事件处理器,导致服务端每发一次 "game-found-status",所有残留监听器都会响应,从而连续触发多次 alert()。
✅ 正确解法:将事件监听逻辑移至 useEffect 中,并利用其清理函数确保组件卸载时自动解绑:
useEffect(() => {
const handleGameFound = (gameFound: boolean) => {
if (gameFound) {
navigate("/player/lobby");
} else {
alert("No game found with this pin.");
}
};
socket.on("game-found-status", handleGameFound);
// 清理:组件卸载或 effect 重运行前移除监听器
return () => {
socket.off("game-found-status", handleGameFound);
};
}, [navigate]); // 依赖项中包含 navigate 确保闭包安全(若 navigate 变化需重新绑定)⚠️ 注意事项:

总结:React 组件中动态绑定外部事件(尤其是 Socket.IO、EventBus、addEventListener)必须严格遵循「声明式生命周期管理」原则——用 useEffect 注册 + 清理函数解绑。这是避免内存泄漏与逻辑错乱的关键实践。