React MSAL自定义Hook问题:获取Bearer Token后请求消失
问题诊断与解决方案
核心问题
你的自定义Hook存在两个关键问题,导致React渲染异常、请求被阻塞:
- 在组件渲染阶段直接调用
setToken和异步函数refreshToken:React不允许在渲染过程中触发状态更新,这会引发无限重渲染循环,最终导致React暂停后续渲染逻辑,包括HTTP请求的发送。 - 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
相关产品推荐
相关产品推荐

