React-Router-Dom V6中Axios拦截器History.push失效问题求助
问题:React Router v6中Axios拦截器跳转登录页仅修改URL不切换组件
使用react-router-dom v6开发项目时,刷新令牌(refresh token)过期后,在Axios拦截器中调用history("/login")仅修改了URL地址,但页面组件并未切换。相关代码如下:
现有代码
index.js
<Provider store={store}> <BrowserRouter history={history}> <App /> </BrowserRouter> </Provider>
App.js
<div className="App"> <LoadingPage /> <FetchProfile /> <AlertMessages /> <Routes> <Route path="/" element={<MainLayout />}> <Route path="/users" element={<UsersPage />} /> <Route path="/agent" element={<AgentPage />} /> <Route path="/server-database" element={<ServerDBPage />} /> <Route path="/customers" element={<CustomersPage />} /> <Route path="/agmdb" element={<AgmdbPage />} /> <Route path="/profile" element={<ProfilePage />} /> <Route path="/userlogs" element={<UserLogsPage />} /> </Route> <Route path="/login" element={<LoginLayout />} /> </Routes> </div>
history.js
import { createBrowserHistory } from 'history'; const history = createBrowserHistory() export default history;
Axios拦截器代码
axiosClient.interceptors.response.use((response) => { return handleResponse(response) }, (error) => { const originalRequest = error.config; if (error.response.status === 401 && !originalRequest._retry) { if (isRefreshing) { return new Promise(function (resolve, reject) { failedQueue.push({ resolve, reject }) }).then(token => { originalRequest.headers['Authorization'] = 'Bearer ' + token; return axiosClient.request(originalRequest); }).catch(err => { return Promise.reject(err); }) } originalRequest._retry = true; isRefreshing = true; const refreshToken = LocalStorageService.getRefreshToken(); return new Promise(function (resolve, reject) { axios.post(`${process.env.REACT_APP_API_URL}/auth/refreshtoken`, { refreshtoken: refreshToken }) .then((res) => { const { data } = res; LocalStorageService.setToken(data.response); axios.defaults.headers.common['Authorization'] = 'Bearer ' + data.response.accessToken; originalRequest.headers['Authorization'] = 'Bearer ' + data.response.accessToken; processQueue(null, data.response.accessToken); resolve(axiosClient.request(originalRequest)); }) .catch((err) => { console.log(err); const { status, data } = err.response; if (status === 401) { history("/login") } if (data && (data.error.code === "REFRESH_TOKEN_INVALID")) { history("/login") } processQueue(err, null); reject(err); }) .finally(() => { isRefreshing = false }) }) } return Promise.reject(handleError(error)); });
问题原因
react-router-dom v6的<BrowserRouter>不再接受history属性,你手动创建的history实例与路由内部维护的导航实例完全独立。调用自定义history的跳转方法只会修改浏览器URL,但无法触发路由系统的组件更新。
解决方案
方案1:使用Context共享导航方法(兼容v6全版本)
- 移除自定义history.js,创建导航上下文:
// NavigationContext.js import { createContext, useContext, useNavigate } from 'react-router-dom'; const NavigationContext = createContext(); export const NavigationProvider = ({ children }) => { const navigate = useNavigate(); return ( <NavigationContext.Provider value={{ navigate }}> {children} </NavigationContext.Provider> ); }; export const useNavigation = () => useContext(NavigationContext);
- 修改index.js,移除
history属性:
<Provider store={store}> <BrowserRouter> <App /> </BrowserRouter> </Provider>
- 在App.js中包裹NavigationProvider:
import { NavigationProvider } from './NavigationContext'; import { useEffect } from 'react'; import { setNavigate } from './axiosClient'; import { useNavigation } from './NavigationContext'; function App() { const { navigate } = useNavigation(); useEffect(() => { // 将导航方法传递给Axios拦截器 setNavigate(navigate); }, [navigate]); return ( <NavigationProvider> <div className="App"> <LoadingPage /> <FetchProfile /> <AlertMessages /> <Routes> {/* 原有路由配置保持不变 */} </Routes> </div> </NavigationProvider> ); }
- 修改Axios拦截器,添加导航方法接收逻辑:
// axiosClient.js import axios from 'axios'; export const axiosClient = axios.create({ // 你的基础配置 }); let navigate; export const setNavigate = (nav) => { navigate = nav; }; // 拦截器代码修改跳转逻辑 axiosClient.interceptors.response.use((response) => { return handleResponse(response) }, (error) => { // ... 原有逻辑不变 .catch((err) => { console.log(err); const { status, data } = err.response; if (status === 401 || (data && data.error.code === "REFRESH_TOKEN_INVALID")) { // 使用共享的navigate方法跳转 navigate("/login"); // 清除本地令牌,避免重复触发 LocalStorageService.clearToken(); } processQueue(err, null); reject(err); }) // ... 原有逻辑不变 });
方案2:使用RouterProvider(v6.4+推荐)
- 重构路由配置,使用
createBrowserRouter:
// index.js import { createBrowserRouter, RouterProvider } from 'react-router-dom'; import { Provider } from 'react-redux'; import store from './store'; import MainLayout from './MainLayout'; import LoginLayout from './LoginLayout'; // 导入其他页面组件 const router = createBrowserRouter([ { path: "/", element: <MainLayout />, children: [ { path: "/users", element: <UsersPage /> }, { path: "/agent", element: <AgentPage /> }, { path: "/server-database", element: <ServerDBPage /> }, { path: "/customers", element: <CustomersPage /> }, { path: "/agmdb", element: <AgmdbPage /> }, { path: "/profile", element: <ProfilePage /> }, { path: "/userlogs", element: <UserLogsPage /> }, ] }, { path: "/login", element: <LoginLayout /> } ]); ReactDOM.createRoot(document.getElementById('root')).render( <Provider store={store}> <RouterProvider router={router} /> </Provider> );
- 修改Axios拦截器,直接使用router实例跳转:
// axiosClient.js import { router } from './index'; // 拦截器中替换跳转逻辑 if (status === 401 || (data && data.error.code === "REFRESH_TOKEN_INVALID")) { router.navigate("/login"); LocalStorageService.clearToken(); }
内容的提问来源于stack exchange,提问作者phu.duongquoc
相关产品推荐
相关产品推荐

