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

Axios拦截器调用Auth0 Hook报错:无效Hook调用问题排查

问题解决:Auth0接入后Axios拦截器Hook调用错误

问题场景

使用Auth0登录应用后,调用第三方API获取用户设置时,为请求添加Auth0的access_token后触发错误:

Uncaught (in promise) Error: Invalid hook call. Hooks can only be called inside of the body of a function component.

移除拦截器后API可正常调用,但因缺少access_token导致认证失败。

相关代码片段:

登录组件代码

export default observer(function LoginMenu() {
const { currentUserStore: { login }} = useStore();
const { loginWithRedirect, isAuthenticated, logout } = useAuth0();

useEffect(() => {
    if (isAuthenticated) {
        login()
    }
  }, [isAuthenticated])
  
  const handleLogin = async () => {
    await loginWithRedirect({
        prompt: "login",
        appState: {
            returnTo: "/callback",
        },
    });
  }
  
  // ... 其他代码
})

Login函数

login = async () => {
    this.loading = true;
    try {
        console.log("Calling API to get currentUser");
        var user = await agent.CurrentUserApi.get();

        console.log("currentUser: ", user);
    } catch(error) {
        runInAction(() => this.loading = false);
        throw error;
    }
}

有问题的Axios拦截器

axios.interceptors.request.use(config => {
const { getAccessTokenSilently } = useAuth0();

config.headers = config.headers ?? {};
const token = getAccessTokenSilently();
if (token) {
    config.headers.Authorization = `Bearer ${token}`
}
return config;
})

错误原因

React Hook必须遵循调用规则:只能在函数组件的顶层作用域或自定义Hook的顶层作用域中调用。你直接在Axios拦截器这个普通函数内部使用useAuth0() Hook,违反了这一规则,导致React无法正确追踪Hook的调用上下文,从而抛出错误。

另外,原拦截器中getAccessTokenSilently()是异步函数,直接赋值const token = getAccessTokenSilently()得到的是Promise对象,而非实际的token字符串,即使Hook调用合法,这里也会导致请求头设置错误。

解决方案

方案一:在顶层组件中初始化带Auth的Axios拦截器

在应用的顶层函数组件(如App.js)中,合法使用useAuth0 Hook,并在useEffect中创建Axios请求拦截器:

import { useAuth0 } from '@auth0/auth0-react';
import axios from 'axios';
import { useEffect } from 'react';

function App() {
  const { getAccessTokenSilently } = useAuth0();

  useEffect(() => {
    // 创建拦截器并保存ID,用于卸载时清理
    const requestInterceptorId = axios.interceptors.request.use(async (config) => {
      config.headers = config.headers ?? {};
      try {
        // 异步获取token
        const token = await getAccessTokenSilently();
        if (token) {
          config.headers.Authorization = `Bearer ${token}`;
        }
      } catch (err) {
        console.error('获取Auth0 token失败:', err);
      }
      return config;
    });

    // 组件卸载时移除拦截器,避免内存泄漏
    return () => {
      axios.interceptors.request.eject(requestInterceptorId);
    };
  }, [getAccessTokenSilently]);

  // ... 渲染其他组件
}

方案二:封装自定义Hook创建带认证的Axios实例

如果需要更灵活的控制,可封装一个自定义Hook,返回已配置认证拦截器的Axios实例:

import { useAuth0 } from '@auth0/auth0-react';
import axios from 'axios';
import { useEffect, useRef } from 'react';

function useAxiosWithAuth() {
  const { getAccessTokenSilently } = useAuth0();
  const axiosInstance = useRef(axios.create()).current;

  useEffect(() => {
    const interceptorId = axiosInstance.interceptors.request.use(async (config) => {
      config.headers = config.headers ?? {};
      const token = await getAccessTokenSilently();
      if (token) {
        config.headers.Authorization = `Bearer ${token}`;
      }
      return config;
    });

    return () => {
      axiosInstance.interceptors.request.eject(interceptorId);
    };
  }, [getAccessTokenSilently]);

  return axiosInstance;
}

// 在组件中使用示例
function UserSettings() {
  const axiosWithAuth = useAxiosWithAuth();

  const fetchUserSettings = async () => {
    try {
      const response = await axiosWithAuth.get('/api/current-user');
      // 处理用户数据
    } catch (err) {
      console.error('获取用户设置失败:', err);
    }
  };

  return <button onClick={fetchUserSettings}>获取用户设置</button>;
}

这两种方案都严格遵循了React Hook的调用规则,同时正确处理了getAccessTokenSilently()的异步逻辑,确保请求头能带上有效的access_token。

内容的提问来源于stack exchange,提问作者Lex

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 22:20:31