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

React MSAL自定义Hook问题:获取Bearer Token后请求消失

问题诊断与解决方案

核心问题

你的自定义Hook存在两个关键问题,导致React渲染异常、请求被阻塞:

  1. 在组件渲染阶段直接调用setToken和异步函数refreshToken:React不允许在渲染过程中触发状态更新,这会引发无限重渲染循环,最终导致React暂停后续渲染逻辑,包括HTTP请求的发送。
  2. token过期检查逻辑位置错误:你在Hook的顶层代码中直接判断token过期并执行状态更新,每次组件渲染都会触发这段逻辑,形成死循环。

修正后的代码

import React from 'react';
import { useIsAuthenticated, useMsal } from '@azure/msal-react';
import config from '../../config';

export const useMsalToken = () => {
    const { instance, accounts } = useMsal();
    const isAuthenticated = useIsAuthenticated();
    const [token, setToken] = React.useState<{ token: string, expiresOn: Date | null } | null>(null);

    const refreshToken = async () => {
        if (!isAuthenticated || !Array.isArray(accounts) || accounts.length === 0) return;
        const account = accounts[0];
        
        try {
            const response = await instance.acquireTokenSilent({
                scopes: [`api://${config.azureAd.clientId}/user_impersonation`],
                account
            });
            const { accessToken, expiresOn } = response;
            
            if (accessToken) {
                setToken({
                    token: accessToken,
                    expiresOn
                });
            }
        } catch (err: any) {
            if (err?.errorCode && ['consent_required', 'interaction_required', 'login_required', 'invalid_grant'].includes(err.errorCode)) {
                instance.loginRedirect();
            }
        }
    };

    // 处理初始token获取和账户/认证状态变化
    React.useEffect(() => {
        refreshToken();
    }, [accounts, isAuthenticated, instance]);

    // 处理token过期自动刷新
    React.useEffect(() => {
        if (!token?.expiresOn) return;
        
        const currentDate = new Date();
        // 提前5分钟刷新,避免token刚好过期时请求失败
        const refreshThreshold = 5 * 60 * 1000;
        const timeUntilExpiry = token.expiresOn.getTime() - currentDate.getTime() - refreshThreshold;

        if (timeUntilExpiry <= 0) {
            refreshToken();
            return;
        }

        // 设置定时器,到期前自动刷新
        const timer = setTimeout(refreshToken, timeUntilExpiry);
        return () => clearTimeout(timer);
    }, [token, instance, accounts, isAuthenticated]);

    return token?.token || '';
};

额外注意事项

  • Axios拦截器中处理token为空的情况:如果token还未获取到(比如页面刚加载时),拦截器应该等待token就绪后再发送请求,或者直接抛出错误提示用户。示例:
    import axios from 'axios';
    import { useMsalToken } from './useMsalToken';
    
    const useApi = () => {
        const token = useMsalToken();
        const api = axios.create({ baseURL: '/api' });
    
        React.useEffect(() => {
            const requestInterceptor = api.interceptors.request.use(
                (config) => {
                    if (token) {
                        config.headers.Authorization = `Bearer ${token}`;
                    }
                    return config;
                },
                (error) => Promise.reject(error)
            );
    
            return () => {
                api.interceptors.request.eject(requestInterceptor);
            };
        }, [token]);
    
        return api;
    };
    
  • 避免在拦截器中直接调用Hook:确保Hook只在React组件或自定义Hook中使用,不要在普通工具函数中调用useMsalToken。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 18:57:38