Redux Toolkit fetchBaseQuery拦截器401跳转时useNavigate报错如何处理
问题根因
useNavigate是React专属Hook,仅支持在函数组件顶层、自定义Hook内部调用,你在utils.ts模块顶层直接调用useNavigate()不符合Hook调用规则,因此触发了无效Hook调用报错。
最优解决方案(React Router v6 官方推荐)
React Router v6 推出的createBrowserRouter支持直接导出路由实例,在非组件环境下直接调用实例的navigate方法实现跳转,无需额外中间层适配。
- 调整路由配置,导出路由实例
// router.ts 路由配置文件 import { createBrowserRouter, RouterProvider } from 'react-router-dom'; import Login from './pages/Login'; import Home from './pages/Home'; // 其他页面导入 // 导出路由实例 export const router = createBrowserRouter([ { path: '/login', element: <Login /> }, { path: '/', element: <Home /> }, // 其余路由配置 ]); // 入口文件使用路由实例 // main.tsx 中示例代码 // import { router } from './router'; // root.render(<RouterProvider router={router} />);
- 改造
authInterceptor中间件,直接使用路由实例跳转
//authInterceptor.ts import { isRejectedWithValue, Middleware } from '@reduxjs/toolkit'; import { router } from './router'; // 导入上面导出的路由实例 const authInterceptor: Middleware = () => (next) => (action) => { if (isRejectedWithValue(action) && action.payload.status === 401) { console.log('401 error'); // 加replace: true避免用户点后退回到触发401的页面,优化体验 router.navigate('/login', { replace: true }); } return next(action); }; export default authInterceptor;
兼容方案(仍使用BrowserRouter组件包裹的场景)
如果你的项目暂时还是用传统的<BrowserRouter>组件包裹应用的方式,可以通过全局变量存储navigate引用的方式实现非组件内跳转:
- 封装全局navigate工具
// navigateUtils.ts import { NavigateFunction } from 'react-router-dom'; let globalNavigate: NavigateFunction | null = null; export const setGlobalNavigate = (navigate: NavigateFunction) => { globalNavigate = navigate; }; export const getGlobalNavigate = () => { if (!globalNavigate) throw new Error('请先在应用根组件初始化globalNavigate'); return globalNavigate; };
- 在根组件初始化全局navigate
// App.tsx 应用根组件 import { useNavigate } from 'react-router-dom'; import { setGlobalNavigate } from './navigateUtils'; import { useEffect } from 'react'; function App() { const navigate = useNavigate(); useEffect(() => { setGlobalNavigate(navigate); }, [navigate]); // 其余根组件逻辑 return <>{/* 应用内容、路由出口等 */}</> }
- 改造中间件
//authInterceptor.ts import { isRejectedWithValue, Middleware } from '@reduxjs/toolkit'; import { getGlobalNavigate } from './navigateUtils'; const authInterceptor: Middleware = () => (next) => (action) => { if (isRejectedWithValue(action) && action.payload.status === 401) { console.log('401 error'); getGlobalNavigate()('/login', { replace: true }); } return next(action); }; export default authInterceptor;
内容的提问来源于stack exchange,提问作者murari_sabavath
相关产品推荐
相关产品推荐

