如何自动更新accessToken并重建Socket连接适配令牌变更?
解决方案
问题根源分析
你的代码在模块初始化时就固定获取了一次accessToken并创建Socket连接,后续localStorage中令牌更新时不会触发重新创建连接;同时直接导出单例Socket在React中使用,会因为实例生命周期与组件不匹配,导致事件监听异常。
重构Socket工具类
将单例Socket改为可动态创建、更新的实例管理模式,确保令牌变更时能重建连接:
import io from "socket.io-client"; // 保存当前Socket实例 let currentSocket = null; // 创建新Socket连接(自动断开旧连接) function createSocket(token) { // 清理旧连接 if (currentSocket) { currentSocket.disconnect(); } const newSocket = io("https://localhost:3000", { extraHeaders: { Authorization: `Bearer ${token}`, }, }); // 绑定通用事件监听 setupSocketListeners(newSocket); currentSocket = newSocket; return newSocket; } // 绑定通用错误/连接事件 function setupSocketListeners(socket) { socket.on("connect", () => console.log("Socket connected")); socket.on("connect_error", (err) => console.error("Socket连接错误:", err)); socket.on("connect_timeout", (timeout) => console.error("Socket连接超时:", timeout)); socket.on("error", (err) => console.error("Socket错误:", err)); } // 更新令牌并重建Socket function updateSocketToken(newToken) { return createSocket(newToken); } // 获取当前Socket实例 function getCurrentSocket() { return currentSocket; } export { createSocket, updateSocketToken, getCurrentSocket };
处理令牌变更场景
1. 主动触发(推荐)
在登录/登出逻辑中,主动调用updateSocketToken更新连接:
// 登录成功后 async function login(credentials) { const res = await fetch("/api/login", { method: "POST", body: JSON.stringify(credentials), headers: { "Content-Type": "application/json" }, }); const { accessToken } = await res.json(); window.localStorage.setItem("accessToken", accessToken); // 更新Socket连接 updateSocketToken(accessToken); } // 登出时 function logout() { window.localStorage.removeItem("accessToken"); if (currentSocket) { currentSocket.disconnect(); currentSocket = null; } }
2. 跨标签页监听
如果需要监听其他标签页的令牌变更,添加storage事件监听(同页面修改不会触发该事件):
// 在Socket工具类中添加 window.addEventListener("storage", (e) => { if (e.key === "accessToken") { if (e.newValue) { updateSocketToken(e.newValue); } else { currentSocket?.disconnect(); currentSocket = null; } } });
React组件中正确使用
通过自定义Hook管理Socket实例,确保组件与Socket生命周期同步:
import { useEffect, useState } from "react"; import { createSocket, updateSocketToken } from "./socketUtils"; function useSocket() { const [socket, setSocket] = useState(null); useEffect(() => { // 组件挂载时初始化Socket const initialToken = window.localStorage.getItem("accessToken"); if (initialToken) { setSocket(createSocket(initialToken)); } // 监听自定义令牌更新事件(配合登录逻辑触发) const handleTokenUpdate = (e) => { const newSocket = updateSocketToken(e.detail); setSocket(newSocket); }; window.addEventListener("tokenUpdated", handleTokenUpdate); // 组件卸载时清理Socket return () => { window.removeEventListener("tokenUpdated", handleTokenUpdate); socket?.disconnect(); }; }, []); return socket; } // 业务组件示例 function ChatRoom() { const socket = useSocket(); useEffect(() => { if (!socket) return; // 绑定组件专属事件 const handleMessage = (msg) => console.log("收到消息:", msg); socket.on("message", handleMessage); // 清理事件监听 return () => socket.off("message", handleMessage); }, [socket]); return <div>聊天室</div>; }
关键改进点
- 避免模块初始化时固定创建Socket,改为动态生成实例
- 确保令牌变更时断开旧连接、创建新连接
- React中用Hook管理Socket,解决实例生命周期与组件不匹配导致的监听异常
内容的提问来源于stack exchange,提问作者xalawo
相关产品推荐
相关产品推荐

