You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 17:25:18