Next.js中Axios拦截器用useRouter触发无效Hook调用求助
解决Next.js 14中Axios拦截器捕获401时的导航问题
问题核心
你遇到的三个问题本质原因如下:
- useRouter无效Hook调用:
useRouter是React Hook,必须遵循Hook规则——只能在组件或自定义Hook的顶层调用,且不能在异步回调、拦截器这类非Hook/组件的逻辑中直接使用。另外你的AxiosInterceptorInstance没有以use开头,React不会将其识别为自定义Hook,调用时也会触发Hook规则报错。 - redirect方法无效:Next.js的
redirect仅适用于Server Components、路由处理函数(如page.tsx的Server Component)或Server Actions,客户端拦截器属于客户端运行时,无法触发该方法。 - window.location无限循环:跳转到
/login后,若页面仍会发起需要权限的请求(比如布局组件里的请求),会再次触发401拦截,导致循环跳转。
可行解决方案
方案1:基于客户端组件的Axios拦截器(推荐)
将Axios实例的创建和拦截器逻辑放在客户端组件中,用useRef保存实例避免重复创建,同时在跳转前判断当前路径,防止循环:
'use client'; import axios from "axios"; import { useRouter, usePathname } from "next/navigation"; import toast from "react-hot-toast"; import { useEffect, useRef } from "react"; // 自定义Hook,必须以use开头 export const useAxiosInterceptor = () => { const router = useRouter(); const pathname = usePathname(); const axiosInstanceRef = useRef(null); useEffect(() => { // 只创建一次Axios实例 if (!axiosInstanceRef.current) { const instance = axios.create({ baseURL: process.env.NEXT_PUBLIC_API_URL, }); // 请求拦截器 instance.interceptors.request.use( (config) => { const accessToken = localStorage.getItem("auth_token"); if (accessToken && config.headers) { config.headers.Authorization = `Bearer ${accessToken}`; } return config; }, (error) => Promise.reject(error) ); // 响应拦截器 instance.interceptors.response.use( (response) => response, (error) => { if (error.response?.status === 401) { toast.error(error.response.data.message, { duration: 4500 }); // 判断当前路径不是/login时再跳转,避免循环 if (pathname !== "/login") { router.push("/login"); // 可选:清除无效token localStorage.removeItem("auth_token"); } } else if (error.response?.status === 422) { toast.error(error.response.data.errors[0], { duration: 2500 }); } else if (error.response?.status === 500) { toast.error("服务器出错", { duration: 2500 }); } else { toast.error(`${error.response?.statusText || "请求失败"} | ${error.message}`); } return Promise.reject(error); } ); axiosInstanceRef.current = instance; } // 组件卸载时移除拦截器 return () => { if (axiosInstanceRef.current) { axiosInstanceRef.current.interceptors.request.clear(); axiosInstanceRef.current.interceptors.response.clear(); } }; }, [router, pathname]); return axiosInstanceRef.current; };
使用方式:在客户端组件中调用该Hook获取Axios实例
'use client'; import { useAxiosInterceptor } from '@/utils/axios-interceptor'; const MyComponent = () => { const axiosInstance = useAxiosInterceptor(); const fetchData = async () => { try { const res = await axiosInstance.get("/api/data"); // 处理数据 } catch (err) { // 错误已被拦截器处理 } }; return <button onClick={fetchData}>获取数据</button>; };
方案2:纯客户端全局导航(无Hook依赖)
如果不想依赖React Hook,可以直接用window.location但加严格的路径判断,同时清除无效token避免重复触发401:
// 响应拦截器中修改401逻辑 case 401: toast.error(error.response.data.message, { duration: 4500 }); localStorage.removeItem("auth_token"); // 只有当前路径不是/login时才跳转 if (window.location.pathname !== "/login") { window.location.href = "/login"; } break;
注意:这种方式适合全局Axios实例,但要确保/login页面不会发起需要权限的请求,否则仍可能循环。
关键注意点
- 自定义Hook必须以
use开头,确保React识别并遵循Hook规则。 - 用
useRef保存Axios实例,避免组件重新渲染时重复创建实例和拦截器。 - 跳转前判断当前路径,是解决无限循环的核心。
- 客户端代码必须加
'use client'指令(Next.js 13+ App Router)。
内容的提问来源于stack exchange,提问作者Tapan Das
相关产品推荐
相关产品推荐

