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

React-router-dom v6 useNavigate跳转时Axios请求拦截器失效问题

问题分析

你遇到的核心问题是:Axios请求拦截器已在组件的useEffect中执行注册,但跳转页面后的请求未触发拦截器,导致Authorization头缺失。根源在于拦截器注册时机与请求发起时机不匹配,以及组件挂载/卸载过程中拦截器被重复操作:

  • 拦截器绑定在React组件的useEffect中,若页面请求在拦截器完成注册前发起(比如组件重新渲染时的异步状态更新延迟),就会出现拦截器未触发的情况。
  • 每次组件挂载都会注册新拦截器,卸载时又会移除旧拦截器,若路由跳转导致组件卸载再挂载,请求可能刚好落在拦截器被移除后、新拦截器注册前的窗口。

解决方案

方案1:全局注册拦截器(推荐)

将拦截器从React组件中移出,改为模块加载时全局注册,确保拦截器始终生效。通过全局变量传递navigate函数实现客户端路由跳转:

  1. 修改Axios拦截器文件:
import axios from 'axios';
import KeycloakService from '../services/KeycloakService';

// 全局存储navigate函数
let navigateFn: ((to: string, options?: any) => void) | undefined;

const instance = axios.create();

// 请求拦截器
const reqInterceptor = (request: any) => {
  if (KeycloakService.isLoggedIn()) {
    const token = KeycloakService.getToken();
    request.headers['Authorization'] = `Bearer ${token}`;
  }
  return request;
};

// 响应拦截器
const resInterceptor = (response: any) => {
  return response;
};

// 错误拦截器
const errInterceptor = (error: any) => {
  if (error.response?.status === 401) {
    navigateFn?.('/home');
  }
  return Promise.reject(error);
};

// 模块加载时注册拦截器(仅执行一次)
instance.interceptors.request.use(reqInterceptor, errInterceptor);
instance.interceptors.response.use(resInterceptor, errInterceptor);

// 暴露设置navigate的方法
export const setNavigateFn = (fn: (to: string, options?: any) => void) => {
  navigateFn = fn;
};

export default instance;
  1. 在App.tsx中初始化navigate函数:
import { useEffect, useNavigate } from 'react-router-dom';
import { setNavigateFn } from './hooks/AxiosInterceptor';

function App() {
  const navigate = useNavigate();

  useEffect(() => {
    setNavigateFn(navigate);
  }, [navigate]);

  return (
    <BrowserRouter>
      <AuthProvider>
        <Routes>
          {/* 你的路由配置 */}
        </Routes>
      </AuthProvider>
    </BrowserRouter>
  );
}

export default App;
  1. 删除原有的AxiosInterceptor组件,拦截器已全局生效,无需再通过组件控制渲染时机。

方案2:保留组件,确保拦截器仅注册一次

若需保留AxiosInterceptor组件,通过全局标记避免重复注册,并移除卸载时的拦截器移除逻辑:

import axios from 'axios';
import { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import KeycloakService from '../services/KeycloakService';

const instance = axios.create();
// 全局标记:是否已注册拦截器
let isInterceptorRegistered = false;

const AxiosInterceptor = ({ children }: any) => {
  const [isSet, setIsSet] = useState(false);
  const navigate = useNavigate();

  useEffect(() => {
    if (!isInterceptorRegistered) {
      const reqInterceptor = (request: any) => {
        if (KeycloakService.isLoggedIn()) {
          const token = KeycloakService.getToken();
          request.headers['Authorization'] = `Bearer ${token}`;
        }
        return request;
      };

      const resInterceptor = (response: any) => {
        return response;
      };

      const errInterceptor = (error: any) => {
        if (error.response?.status === 401) {
          navigate('/home');
        }
        return Promise.reject(error);
      };

      instance.interceptors.request.use(reqInterceptor, errInterceptor);
      instance.interceptors.response.use(resInterceptor, errInterceptor);
      isInterceptorRegistered = true;
    }

    setIsSet(true);

    // 移除卸载时的拦截器移除逻辑,避免拦截器被意外销毁
    return () => {};
  }, [navigate]);

  return isSet && children;
};

export default instance;
export { AxiosInterceptor };

验证要点

  1. 确保所有Axios请求都使用从拦截器文件导出的instance,而非直接导入axios包。
  2. 在拦截器中添加console.log调试,确认KeycloakService.isLoggedIn()和getToken()能正确返回状态和令牌。
  3. 跳转页面后,查看网络请求的请求头是否包含Authorization字段。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 13:20:33