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

react-router-dom v6.3 axios拦截器history.push跳转无效问题

react-router-dom v6.3.0 全局history.push 触发路由跳转失效修复

问题根因

  • history库版本不匹配:react-router-dom@6.3.0 内部依赖的是history v5版本的事件监听、路由更新逻辑,如果你项目中自行安装的是history v6版本,自己通过createBrowserHistory创建的实例API和Router内部逻辑不兼容,会出现执行history.push时无路由响应的问题,和路由无关的日志打印、localStorage清理逻辑不受影响,可正常执行。
  • 循环引用导致实例异常:你将history实例定义在App.tsx组件文件中导出,在axios拦截器文件中引入App模块,很容易在模块初始化阶段拿到未完成初始化的history实例,调用方法自然不会生效。
  • 现有拦截器逻辑存在隐性bug:res变量定义在try块作用域内,catch块外直接访问会抛出引用错误;refresh失败后未中断流程,仍会重试原请求,可能触发无效请求或死循环。

修复步骤

  1. 安装匹配版本的history依赖
# npm
npm install history@5
# yarn
yarn add history@5
  1. 抽离全局history实例为独立文件,避免循环引用
    新建src/router/history.ts文件,单独维护history实例:
import { createBrowserHistory } from 'history';
export const history = createBrowserHistory();
  1. 修正App.tsx的引入逻辑
import { unstable_HistoryRouter as HistoryRouter } from 'react-router-dom';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { history } from './router/history';
import Views from './Views';

const queryClient = new QueryClient();

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <HistoryRouter history={history}>
        <Views />
      </HistoryRouter>
    </QueryClientProvider>
  );
}

export default App;
  1. 修正axios拦截器逻辑,补全错误中断、防死循环处理
import axios from 'axios';
import { history } from './router/history';
// 替换为你项目里的实际baseUrl、refresh实例配置
const baseUrl = import.meta.env.VITE_API_BASE_URL;
const axiosInstance = axios.create({ baseURL: baseUrl });
const axiosInstanceRefresh = axios.create({ baseURL: baseUrl });

const refreshAccessToken = async () => {
  try {
    const res = await axiosInstanceRefresh.post(
      'auth/refresh',
      null,
      { headers: { 'Authorization': `Bearer ${localStorage.getItem('refreshToken')}` } }
    )
    // 存储新的token对
    const { accessToken, refreshToken } = res.data;
    localStorage.setItem('accessToken', accessToken);
    localStorage.setItem('refreshToken', refreshToken);
    return accessToken;
  } catch (err) {
    console.log('refresh token已失效,跳转至首页');
    localStorage.removeItem('accessToken');
    localStorage.removeItem('refreshToken');
    // 用replace代替push,避免用户点返回回到受保护路由
    history.replace('/');
    // 抛出错误中断后续重试流程
    return Promise.reject(err);
  }
}

axiosInstance.interceptors.request.use(async request => {
  request.headers.Authorization = `Bearer ${localStorage.getItem('accessToken')}`;
  return request;
}, error => Promise.reject(error));

axiosInstance.interceptors.response.use(response => response, async error => {
  const originalRequest = error.config;
  // 加_retry标记防止刷新token死循环
  if (error.response?.status === 401 && !originalRequest._retry) {
    originalRequest._retry = true;
    const newAccessToken = await refreshAccessToken();
    originalRequest.headers.Authorization = `Bearer ${newAccessToken}`;
    return axiosInstance(originalRequest);
  }
  return Promise.reject(error);
});

export default axiosInstance;

额外优化建议

  • 鉴权失效跳转优先用history.replace而非push,避免用户通过浏览器返回键重新进入触发401的受保护页面,造成重复跳转。
  • 拦截器中一定要加重试标记,否则401场景下会无限触发token刷新逻辑,造成接口死循环调用。
  • 全局路由相关的工具实例(history、路由常量等)尽量抽离到独立的router目录文件维护,不要在组件文件中导出给非组件模块使用,避免循环引用问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 15:12:33