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

如何自动更新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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 05:07:39