Axios拦截器遇401时setToken无法置空,请求排查问题
问题排查:401状态码时setToken()未生效的原因及修复方案
核心错误原因
- React Hook使用违规:
useAuthContext()是React自定义Hook,必须在React组件或其他自定义Hook的顶层调用。你在axios拦截器(组件外的普通JS函数)中调用它,违反了React Hook规则,此时获取到的setToken是Context的默认空函数,而非AuthContextProvider中定义的真实函数,所以调用后毫无效果。
代码问题拆解
axiosConfig.js中的违规调用
axiosClient.interceptors.response.use( (response) => { return response; }, (error) => { const { response } = error; if (error.message === "Network Error") { alert("network error"); } if (response.status === 401) { const{setToken} = useAuthContext(); // ❌ 组件外调用Hook,获取的是默认空函数 alert('unauthorized'); localStorage.removeItem("ACCESS_TOKEN"); setToken(); } throw error; } );
AuthContextProvider.jsx逻辑无问题,但无法被组件外代码直接调用Hook
你的Context定义和setToken逻辑是正确的,但Hook的调用场景限制导致拦截器无法获取到有效函数。
修复方案
方案1:将拦截器迁移到React组件内(推荐)
在全局组件(如App)中通过useEffect挂载拦截器,此时可合法调用Hook:
import { useEffect } from 'react'; import { useAuthContext } from './AuthContextProvider'; import axiosClient from './axiosConfig'; function App() { const { setToken } = useAuthContext(); useEffect(() => { const interceptor = axiosClient.interceptors.response.use( (response) => response, (error) => { const { response } = error; if (error.message === "Network Error") { alert("network error"); } if (response?.status === 401) { alert('unauthorized'); setToken(null); // 直接调用有效setToken,内部已自动处理localStorage清除 } throw error; } ); // 组件卸载时移除拦截器,避免内存泄漏 return () => axiosClient.interceptors.response.eject(interceptor); }, [setToken]); // ...其他组件渲染逻辑 }
注意:无需手动调用localStorage.removeItem,你的setToken函数内部已经处理了这个逻辑。
方案2:通过全局回调传递setToken
如果不想迁移拦截器,可在Context初始化时将setToken挂载到全局对象(注意安全风险):
- 修改AuthContextProvider.jsx:
export const AuthContextProvider = ({ children }) => { const [user, setUser] = useState({}); const [token, _setToken] = useState(localStorage.getItem('ACCESS_TOKEN')); const setToken = (token) => { _setToken(token) if (token) { localStorage.setItem('ACCESS_TOKEN', token); } else { localStorage.removeItem('ACCESS_TOKEN'); } } // 将setToken挂载到全局window对象 useEffect(() => { window.__authSetToken = setToken; return () => delete window.__authSetToken; }, [setToken]); return ( <AuthContext.Provider value={{ user, token, setUser, setToken }}> {children} </AuthContext.Provider> ) }
- 修改axiosConfig.js:
axiosClient.interceptors.response.use( (response) => { return response; }, (error) => { const { response } = error; if (error.message === "Network Error") { alert("network error"); } if (response.status === 401) { alert('unauthorized'); // 调用全局挂载的有效setToken window.__authSetToken?.call(null, null); } throw error; } );
该方案需注意全局变量的安全性,生产环境谨慎使用。
内容的提问来源于stack exchange,提问作者Ruhul Amin
相关产品推荐
相关产品推荐

