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

React中useNavigate与Axios拦截器触发无效Hook调用错误排查

解决React中Invalid Hook Call错误的方案

错误原因

你在普通函数getAllDocuments里直接调用了自定义HookusePrivateAxios,违反了React Hook的核心规则:Hook只能在函数组件的顶层或其他自定义Hook内部调用,不能在普通函数、循环、条件语句中使用。

解决方案

方案1:在组件内使用Hook并调用API

把API调用逻辑移到函数组件内部,先通过Hook获取Axios实例,再发起请求:

// 示例组件
import usePrivateAxios from '../usePrivateAxios';

function DocumentsPage() {
  const privateAxios = usePrivateAxios();

  const fetchDocuments = async () => {
    try {
      const data = await privateAxios.get('/documents').then(res => res.data);
      // 处理返回的数据
    } catch (err) {
      // 处理请求错误
    }
  };

  return (
    <div>
      <button onClick={fetchDocuments}>获取文档列表</button>
    </div>
  );
}

同时修改documentAPI.js,让它接收Axios实例作为参数:

export const getAllDocuments = (privateAxios, config) => {
  return privateAxios
    .get("/documents", config)
    .then((response) => response.data)
    .catch((error) => {
      throw error;
    });
};

在组件内调用时:

const data = await getAllDocuments(privateAxios);

方案2:重构私有Axios实例,移除Hook依赖

如果不想在组件内处理拦截器逻辑,可以直接给Axios实例配置拦截器,用window.location实现跳转(替代useNavigate):

修改privateAxios.js:

import { axiosPrivate } from "./axios";

// 配置请求拦截器
axiosPrivate.interceptors.request.use(
  (config) => {
    const accessToken = localStorage.getItem("accessToken");

    if (!accessToken) {
      // 直接跳转登录页
      window.location.href = "/admin/login";
    } else {
      config.headers.Authorization = `Bearer ${accessToken}`;
    }

    return config;
  },
  (error) => Promise.reject(error),
);

// 配置响应拦截器
axiosPrivate.interceptors.response.use(
  (response) => response,
  (error) => Promise.reject(error),
);

export default axiosPrivate;

修改documentAPI.js直接导入使用:

import privateAxios from "../privateAxios";

export const getAllDocuments = (config) => {
  return privateAxios
    .get("/documents", config)
    .then((response) => response.data)
    .catch((error) => {
      throw error;
    });
};

方案3:自定义Hook返回封装好的API请求函数

创建一个Hook,直接返回封装后的API请求方法,在组件里调用Hook获取方法后使用:

// useDocumentAPI.js
import { axiosPrivate } from "./axios";
import { useEffect } from "react";
import { useNavigate } from "react-router-dom";

const useDocumentAPI = () => {
  const navigate = useNavigate();

  useEffect(() => {
    const requestInterceptor = axiosPrivate.interceptors.request.use(
      (config) => {
        const accessToken = localStorage.getItem("accessToken");
        if (!accessToken) {
          navigate("/admin/login");
        } else {
          config.headers.Authorization = `Bearer ${accessToken}`;
        }
        return config;
      },
      (error) => Promise.reject(error),
    );

    const responseInterceptor = axiosPrivate.interceptors.response.use(
      (response) => response,
      (error) => Promise.reject(error),
    );

    return () => {
      axiosPrivate.interceptors.request.eject(requestInterceptor);
      axiosPrivate.interceptors.response.eject(responseInterceptor);
    };
  }, [navigate]);

  const getAllDocuments = (config) => {
    return axiosPrivate
      .get("/documents", config)
      .then((res) => res.data)
      .catch((err) => {
        throw err;
      });
  };

  return { getAllDocuments };
};

export default useDocumentAPI;

在组件里使用:

import useDocumentAPI from './useDocumentAPI';

function DocumentsPage() {
  const { getAllDocuments } = useDocumentAPI();

  const fetchData = async () => {
    try {
      const data = await getAllDocuments();
      // 处理数据
    } catch (err) {
      // 处理错误
    }
  };

  return <button onClick={fetchData}>获取文档</button>;
}

关键注意点

  • 所有自定义Hook必须以use开头,且只能在函数组件或其他自定义Hook的顶层调用
  • 非组件环境中实现页面跳转,优先使用window.location或提前创建React Router的history实例

内容的提问来源于stack exchange,提问作者Văn Thuận Nguyễn

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 23:00:03