在Axios拦截器中使用useDispatch触发Invalid hook call错误求助
问题分析与解决
错误原因
你在Axios响应拦截器里直接调用了useDispatch这个React Hook,这违反了React Hook的核心规则:Hook只能在函数组件的顶层作用域,或者自定义Hook内部调用。Axios拦截器是独立于React组件的普通JavaScript函数,不属于组件的渲染流程,所以在这里调用Hook会触发"Invalid hook call"错误。
另外还有两个无效操作:
- 拦截器里返回
<Navigate to="/login" />是没用的,<Navigate>是React组件,只能在组件渲染时使用,拦截器里无法渲染React元素。 - 你注释掉的
// eslint-disable-next-line react-hooks/rules-of-hooks只是掩盖了规则检查,并没有解决根本问题。
解决方案
方法1:直接使用Redux Store实例 dispatch
这是全局拦截场景下最直接的方案,通过导入Redux store实例,直接用store.dispatch触发action,不需要依赖Hook。
步骤1:导出Redux Store
在你的Redux store配置文件(比如store.js)中,导出创建好的store对象:
// store.js import { configureStore } from '@reduxjs/toolkit'; import statusReducer from './slices/statusSlice'; // 替换成你的status reducer路径 export const store = configureStore({ reducer: { status: statusReducer, // 其他reducer... }, });
步骤2:修改Axios拦截器代码
在api.js中导入store,替换useDispatch的使用:
import axios from "axios"; import TokenService from "./token.service"; import { store } from "../store"; // 导入Redux store实例 import { setSTATUS } from "../actions/status"; const secretKey = { apps_id: process.env.REACT_APP_APPS_ID, secret_key: process.env.REACT_APP_API_KEY, }; const instance = axios.create({ baseURL: process.env.REACT_APP_BASE_URL, headers: { "Content-Type": "application/json", token: TokenService.getLocalAccessToken(), }, }); instance.interceptors.request.use( (config) => { const token = TokenService.getLocalAccessToken(); if (token) { config.headers["token"] = token; } return config; }, (error) => Promise.reject(error) ); instance.interceptors.response.use( (res) => res, async (err) => { // 用store.dispatch代替useDispatch if (err.response?.status) { store.dispatch(setSTATUS(err.response.status)); } const originalConfig = err.config; if ( originalConfig.url !== "/api/auth/login" && err.message === "Request failed with status code 401" ) { if ( err.response.data.status === 401 && err.response.data.message === "Error token : jwt expired" ) { originalConfig._retry = true; try { const rs = await instance.post( "/token/generate_token", secretKey ); const token = rs.data.data; TokenService.updateLocalAccessToken(token); TokenService.removeUser(); // 替换Navigate:直接跳转页面 window.location.href = "/login"; return instance(originalConfig); } catch (_error) { return Promise.reject(_error); } } } return Promise.reject(err); } ); export default instance;
方法2:自定义Hook封装Axios(组件级场景可选)
如果需要在组件上下文里使用Axios(比如依赖组件内的状态),可以创建自定义Hook,在Hook内部获取dispatch并配置拦截器:
// useAxios.js import { useDispatch } from 'react-redux'; import axios from 'axios'; import TokenService from './token.service'; import { setSTATUS } from '../actions/status'; export const useAxios = () => { const dispatch = useDispatch(); const instance = axios.create({ baseURL: process.env.REACT_APP_BASE_URL, headers: { "Content-Type": "application/json", token: TokenService.getLocalAccessToken(), }, }); instance.interceptors.response.use( (res) => res, async (err) => { if (err.response?.status) { dispatch(setSTATUS(err.response.status)); } // 其他拦截逻辑... return Promise.reject(err); } ); return instance; };
在组件中使用这个Hook:
import { useAxios } from './useAxios'; const MyComponent = () => { const api = useAxios(); const fetchData = async () => { try { const res = await api.get('/data'); // 处理数据 } catch (err) { // 组件内错误处理 } }; return <button onClick={fetchData}>获取数据</button>; };
关键注意点
- 永远不要在非组件/非自定义Hook的普通函数中调用React Hook,这是Hook规则的核心要求。
- 拦截器中不能直接使用React路由的
<Navigate>或useNavigate,需要跳转时可以用window.location.href,或者在组件的错误捕获逻辑中调用navigate。
内容的提问来源于stack exchange,提问作者JS24
相关产品推荐
相关产品推荐

