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

React项目调用MS Graph API获取日历事件失败问题排查

问题原因

你抛出Invalid hook call错误的核心原因是:React Hook只能在函数组件的顶层作用域调用,不能在类组件的普通方法内部调用。

你的正常运行的importContact.jsx是函数组件,useMsal、useState都放在组件顶层,符合Hook调用规则;而出错的events.jsx是Class组件,你在类的getMicrosoftEvents方法内部调用了useMsal和useState,完全不符合Hook的使用要求,所以执行到这行直接报错中断,后续代码不会运行。

修复方案

方案1:将Events组件改为函数组件(推荐)

把Class组件重构为函数组件,用useEffect替代componentDidMount生命周期,所有Hook放在组件顶层调用,参考代码如下:

...
import { loginRequest, graphConfig } from '../authConfig.jsx';
import { useMsal } from "@azure/msal-react";
import { useState, useEffect } from 'react';

const Events = (props) => {
  // Hook全部放在组件顶层调用
  const { instance, accounts, inProgress } = useMsal();
  const [accessToken, setAccessToken] = useState(null);
  const [graphData, setGraphData] = useState(null);

  useEffect(() => {
    var date = new Date();
    var firstDay = new Date(date.getFullYear(), date.getMonth(), 1);
    var lastDay = new Date(date.getFullYear(), date.getMonth() + 1, 0);
    getMicrosoftEvents(firstDay, lastDay);
  }, []); // 空依赖数组等价于componentDidMount

  const getMicrosoftEvents = (start, end) => {
    console.log('log displayed in console')
    if(!accounts[0]) return; // 先判断是否有登录账号,避免报错
    const request = {
        ...loginRequest,
        account: accounts[0]
    };

    // 静默获取访问令牌后调用Graph接口
    instance.acquireTokenSilent(request).then((response) => {
        console.log('acquireTokenSilent')
        setAccessToken(response.accessToken);
        callMsGraph(response.accessToken, start, end).then(
        response => {
            console.log('microsoft response ', response)
            setGraphData(response);
        }
        );
    }).catch((e) => {
        console.log('microsoft response error ', e)
        instance.acquireTokenPopup(request).then((response) => {
            setAccessToken(response.accessToken);
            callMsGraph(response.accessToken, start, end).then(
            response => {
                console.log('microsoft response ', response)
                setGraphData(response);
            }
            );
        });
    });
  }

  async function callMsGraph(accessToken, start, end) {
    console.log('callMsGraph ')
    const headers = new Headers();
    const bearer = `Bearer ${accessToken}`;
    
    headers.append("Authorization", bearer);

    const options = {
        method: "GET",
        headers: headers
    };
    // 注意:日期需要转成ISO格式,否则Graph API无法识别
    return fetch(`${graphConfig.graphCalendarsEndpoint}?startDateTime=${start.toISOString()}&endDateTime=${end.toISOString()}`,
       options)
        .then(response => {
            // 这里要return response.json(),否则后续拿不到返回值
            return response.json();
        })
        .catch(error => console.log(error));
  }
  // 组件渲染逻辑...
  return <div>日历内容</div>
}

export default Events;

方案2:保留Class组件,使用MSAL提供的HOC包裹

如果你不想改Class组件的结构,可以使用@azure/msal-react提供的withMsal高阶组件来获取msal实例,不需要使用useMsal Hook,同时移除类组件内部的useState调用,改用类组件的this.state管理状态。

额外注意点
  • 原代码中fetch的then回调里没有return response.json(),会导致后续拿到的返回值为undefined,需要补上
  • 传给Graph API的起止时间需要转成ISO 8601格式,否则接口会返回参数错误
  • 调用接口前先判断accounts[0]是否存在,避免用户未登录时调用报错

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 10:06:03