Teams Toolkit构建React项目白屏,问题源于useGraphWithCredential
Teams Toolkit构建后白屏,定位到useGraphWithCredential问题的解决思路
问题描述
使用Teams Toolkit开发应用时,开发阶段页面显示正常,但执行构建后通过serve -s build本地托管出现白屏。排查发现,注释掉useGraphWithCredential相关代码后,构建产物即可正常显示。
相关代码
import { useGraphWithCredential } from "@microsoft/teamsfx-react"; import { Providers, ProviderState } from "@microsoft/mgt-element"; import { TeamsFxProvider } from "@microsoft/mgt-teamsfx-provider"; import { TeamsFxContext } from "./Context"; import { useContext} from "react"; import { tokens, makeStyles, shorthands, Spinner } from "@fluentui/react-components"; import { Tree, TreeItem, TreeItemLayout, } from "@fluentui/react-components/unstable"; const useMenuListContainerStyles = makeStyles({ container: { width: "fit-content", height: "100vw", minWidth: "250px", ...shorthands.padding('20px', '0', '0', '0'), }, treeItem:{ backgroundColor: tokens.colorNeutralBackground1, borderRadius: tokens.borderRadiusMedium } }); export default function Assigments({assignments, setAssignments}) { const { teamsUserCredential } = useContext(TeamsFxContext); const styles = useMenuListContainerStyles(); const provider = new TeamsFxProvider(teamsUserCredential, scope); Providers.globalProvider = provider; Providers.globalProvider.setState(ProviderState.SignedIn); // 注释此部分代码后构建产物正常显示 const { loading, error, data, reload } = useGraphWithCredential( async (graph, teamsUserCredential, scope) => { const classes = await graph.api("/education/me/classes").version('beta').get(); const assignments = await graph.api("/education/me/assignments").version('beta').get(); return { classes, assignments}; }, { scope: [ "AccessReview.ReadWrite.All", "User.Read.All", "EduRoster.ReadBasic", "EduAssignments.ReadWriteBasic", "EduAssignments.Read", "Directory.ReadWrite.All", "Group.Read.All", "Sites.ReadWrite.All", "Group.ReadWrite.All", "GroupMember.Read.All", "User.ReadWrite.All", "Sites.Manage.All", "Sites.FullControl.All" ], credential: teamsUserCredential } ); // 注释上面代码后用以下变量避免报错 // const data = []; // const loading = true; return ( <div className={styles.container}> <Tree aria-label="Tree"> {loading ? <Spinner /> : data && data.classes.value.map( cl => <TreeItem key={cl.id} itemType="branch"> <TreeItemLayout className={assignments.classId == cl.id ? styles.treeItem : ""}>{cl.displayName}</TreeItemLayout> <Tree> {data.assignments.value.map (ass => { if(cl.id == ass.classId && ass.displayName) return( <TreeItem key={ass.id} itemType="leaf" onClick={()=>setAssignments({classId: cl.id, assId: ass.id})} > <TreeItemLayout className={assignments.assId == ass.id ? styles.treeItem : ""}>{ass.displayName}</TreeItemLayout> </TreeItem> ) } )} </Tree> </TreeItem> )} </Tree> </div> ); }
可能原因及解决方案
1. 未定义的scope变量
代码中new TeamsFxProvider(teamsUserCredential, scope)的scope未提前定义,开发环境宽松的错误容忍度可能忽略此问题,但生产构建后会直接抛出错误导致白屏。
修复方案:
将useGraphWithCredential配置中的scope数组提取为变量,再传递给TeamsFxProvider:
const requiredScopes = [ "AccessReview.ReadWrite.All", "User.Read.All", "EduRoster.ReadBasic", "EduAssignments.ReadWriteBasic", "EduAssignments.Read", "Directory.ReadWrite.All", "Group.Read.All", "Sites.ReadWrite.All", "Group.ReadWrite.All", "GroupMember.Read.All", "User.ReadWrite.All", "Sites.Manage.All", "Sites.FullControl.All" ]; // 替换原Provider初始化代码 const provider = new TeamsFxProvider(teamsUserCredential, requiredScopes);
2. 未处理的异步错误
useGraphWithCredential内部的Graph API调用如果抛出错误,未被捕获会导致组件渲染崩溃,出现白屏。
修复方案:
在API调用中添加try-catch,并在组件中处理error状态:
const { loading, error, data, reload } = useGraphWithCredential( async (graph, teamsUserCredential, scope) => { try { const classes = await graph.api("/education/me/classes").version('beta').get(); const assignments = await graph.api("/education/me/assignments").version('beta').get(); return { classes, assignments}; } catch (err) { console.error("Graph API调用失败:", err); throw err; // 保留错误让useGraphWithCredential捕获,或返回默认空数据 } }, { scope: requiredScopes, credential: teamsUserCredential } ); // 渲染时添加错误处理 if (error) { return <div>加载失败:{error.message}</div>; }
3. 组件顶层重复初始化Provider
直接在组件顶层设置Providers.globalProvider会导致每次渲染都重复初始化,引发不必要的错误。
修复方案:
使用useEffect包裹Provider初始化逻辑,仅在依赖变化时执行:
import { useEffect } from "react"; // ... 其他代码 export default function Assigments({assignments, setAssignments}) { const { teamsUserCredential } = useContext(TeamsFxContext); const styles = useMenuListContainerStyles(); const requiredScopes = [/* 权限数组 */]; useEffect(() => { if (teamsUserCredential) { const provider = new TeamsFxProvider(teamsUserCredential, requiredScopes); Providers.globalProvider = provider; Providers.globalProvider.setState(ProviderState.SignedIn); } }, [teamsUserCredential, requiredScopes]); // ... useGraphWithCredential及其他代码 }
4. 本地托管的认证限制
serve -s build本地托管时,无法模拟Teams客户端的认证环境,导致teamsUserCredential无法获取有效凭证,进而引发Graph API调用失败。
修复方案:
不要在本地静态托管环境测试构建后的应用,直接将构建产物部署到Teams允许的环境,或在Teams客户端内加载测试。
内容的提问来源于stack exchange,提问作者Nik May
相关产品推荐
相关产品推荐

