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

如何避免React组件在Axios拦截器刷新Token前跳转登录页?

解决React中Axios刷新Token时组件提前跳转登录页的问题

问题根源

你的组件代码在拦截器完成Token刷新并重新发起请求前,就捕获到了第一次的401错误并执行了跳转逻辑。核心原因是原代码未区分"Token过期可通过Refresh Token刷新"和"真正未授权(Refresh Token也过期)"两种场景,组件直接处理了所有401响应,导致提前跳转。

解决方案

核心思路是将401错误的统一处理交给Axios拦截器,组件只处理最终的请求成功结果或非401类错误,避免提前触发跳转。

1. 优化Axios拦截器逻辑

修改拦截器,让它负责Token刷新、请求重试和登录跳转的完整流程,同时处理多请求并发401的场景:

// 全局变量:标记是否正在刷新Token,保存等待中的请求队列
let isRefreshing = false;
let requestQueue = [];

axios.interceptors.response.use(
  (response) => response,
  async (error) => {
    const { config, response } = error;
    // 仅处理Token过期导致的401,且未在刷新中
    if (response?.status === 401 && !isRefreshing) {
      isRefreshing = true;
      try {
        // 发起Refresh Token请求
        const refreshResp = await axios.post(
          "/api/auth/refresh",
          {},
          { withCredentials: true }
        );

        if (refreshResp.status === 200) {
          const newAccessToken = refreshResp.data.accessToken;
          // 更新全局Authorization请求头
          axios.defaults.headers.common["Authorization"] = `Bearer ${newAccessToken}`;
          // 重新发起队列中所有等待的请求
          requestQueue.forEach(({ resolve }) => resolve(axios(config)));
          requestQueue = [];
          // 重新发起当前请求,返回最终成功结果
          return axios(config);
        }
      } catch (refreshErr) {
        // Refresh Token过期,直接跳转登录页
        window.location.href = "/login"; // 若用React Router,可改用history.push('/login')
        // 清除无效的Token请求头
        delete axios.defaults.headers.common["Authorization"];
        // 拒绝队列中所有等待的请求
        requestQueue.forEach(({ reject }) => reject(refreshErr));
        requestQueue = [];
        return Promise.reject(refreshErr);
      } finally {
        // 重置刷新状态
        isRefreshing = false;
      }
    } else if (response?.status === 401 && isRefreshing) {
      // 已有请求在刷新Token,将当前请求加入队列等待
      return new Promise((resolve, reject) => {
        requestQueue.push({ resolve, reject, config });
      });
    }
    // 非401错误,直接抛给组件处理
    return Promise.reject(error);
  }
);

2. 调整组件与请求函数逻辑

组件不再处理401错误,仅关注请求成功或其他类型错误,同时添加挂载状态判断,避免组件卸载后更新状态导致的警告:

const [pages, setPages] = useState();
const [error, setError] = useState();

const navigate = useNavigate();

useEffect(() => {
  let isComponentMounted = true;

  async function fetchInfo() {
    try {
      const response = await getMyPages();
      if (isComponentMounted) {
        setPages(response.data);
      }
    } catch (err) {
      if (isComponentMounted) {
        // 仅处理非401错误(401已被拦截器处理并跳转)
        setError(t("error.unexpected"));
      }
    }
  }

  fetchInfo();

  // 组件卸载时标记为未挂载
  return () => {
    isComponentMounted = false;
  };
}, [t, navigate]);

// 修改getMyPages,移除内部try/catch,让错误向上抛给组件处理
export async function getMyPages() {
  return await axios.get(`/api/user/mypages`);
}

3. (可选)实现React Router无刷新跳转

若不想用window.location.href导致页面刷新,可创建全局History实例:

// history.js
import { createBrowserHistory } from 'history';
export const history = createBrowserHistory();

在App.js中替换默认Router:

import { Router } from 'react-router-dom';
import { history } from './history';

function App() {
  return (
    <Router history={history}>
      {/* 你的路由组件 */}
    </Router>
  );
}

然后在拦截器中导入并使用:

import { history } from './history';

// 替换window.location.href为:
history.push('/login');

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 05:35:42