React中MSAL认证页面刷新后对象嵌套属性显示undefined的问题
解决React MSAL认证页面刷新后用户属性访问undefined问题
问题说明
刚接触React,开发使用MSAL认证的应用,用UserContext维护用户信息,正常场景运行正常,但页面刷新后出现异常:控制台打印能看到完整的用户对象,但直接访问嵌套属性(如companyName)时返回undefined,直到页面触发重新渲染(比如打开菜单、路由跳转)后才能正常显示,导致用户信息展示区域为空。
问题根源
- 异步操作未触发状态更新:
UserDatasFromMS是同步函数,但内部调用的acquireTokenSilent和callMsGraph都是异步操作,函数执行完直接返回空的ADuserdatas对象;后续异步请求完成后直接修改该对象属性,这种修改不会触发React状态更新,组件自然不会重新渲染读取新数据。 - 初始状态不完整:
datasFromMS初始化为空对象,刷新后组件首次渲染读取的是初始空状态,异步数据回来后没有通过setDatasFromMS更新状态,直到其他操作触发重新渲染,才会读取到被修改后的对象属性。
修复步骤
- 将
UserDatasFromMS改为异步函数,等待MSAL和Graph请求完成后返回完整用户数据。 - 在
UserContextProvider的useEffect中用async/await处理异步逻辑,确保拿到完整数据后再更新状态。 - 初始化
datasFromMS时使用initUser(),保证初始状态结构完整,避免不必要的undefined。 - 避免直接修改对象属性,创建新对象赋值,遵循React状态不可变性原则。
修复后的完整代码
import { useIsAuthenticated } from "@azure/msal-react"; import { createContext, useState, useEffect } from "react"; import { useMsal } from "@azure/msal-react"; import { msalConfig } from "../authConfig"; const initUser = () => ({ ADuserdatas: { homeAccountId: "", accountEnabled: "", companyName: "", createdDateTime: "", department: "", displayName: "", givenName: "", id: "", jobTitle: "", mail: "", mobilePhone: "", officeLocation: "", surname: "", userType: "", }, language: "en" }); // 改为异步函数,返回Promise const UserDatasFromMS = async ({ instance, accounts }) => { const callMsGraph = async (token) => { // 补充Graph API请求实现 const response = await fetch("https://graph.microsoft.com/v1.0/me", { headers: { Authorization: `Bearer ${token}` } }); return response.json(); }; try { const tokenResponse = await instance.acquireTokenSilent({ scopes: ["openid", "profile", "user.read"], account: accounts[0], authority: `https://login.microsoftonline.com/${msalConfig.auth.tenantId}`, }); const graphData = await callMsGraph(tokenResponse.accessToken); // 创建新对象返回,不直接修改原对象 return { ADuserdatas: { homeAccountId: tokenResponse.account.homeAccountId, accountEnabled: graphData.accountEnabled, companyName: graphData.companyName, createdDateTime: graphData.createdDateTime, department: graphData.department, displayName: graphData.displayName, givenName: graphData.givenName, id: graphData.id, jobTitle: graphData.jobTitle, mail: graphData.mail, mobilePhone: graphData.mobilePhone, officeLocation: graphData.officeLocation, surname: graphData.surname, userType: graphData.userType, } }; } catch (error) { console.error("获取用户数据失败:", error); // 可根据需求添加重新登录逻辑,比如instance.loginRedirect() return initUser(); } }; export const UserContext = createContext({}); export const UserContextProvider = ({ children }) => { const { instance, accounts } = useMsal(); const isAuthenticated = useIsAuthenticated(); // 初始化状态为完整结构 const [datasFromMS, setDatasFromMS] = useState(initUser()); useEffect(() => { const fetchUserData = async () => { if (isAuthenticated && accounts.length > 0) { const userData = await UserDatasFromMS({ instance, accounts }); setDatasFromMS(userData); } else { setDatasFromMS(initUser()); } }; fetchUserData(); }, [instance, accounts, isAuthenticated]); return ( <UserContext.Provider value={{ datasFromMS, setDatasFromMS, }} > {children} </UserContext.Provider> ); };
额外说明
- 确保
callMsGraph函数能正常从Microsoft Graph获取用户数据,可根据实际需求调整API端点。 - 添加了错误捕获,处理token静默获取失败的情况(比如token过期),可根据业务需求修改错误处理逻辑。
- 依赖数组添加了
isAuthenticated,确保认证状态变化时重新获取用户数据。
内容的提问来源于stack exchange,提问作者Jennydev
相关产品推荐
相关产品推荐

