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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 10:27:47