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

React Router TS中父组件向子路由组件传递accessToken失败求助

问题:React Router中无法将accessToken传递给子路由组件

我是React和TypeScript新手,尝试在React Router中将accessToken从父组件传递给子路由组件。父组件中能打印看到该密钥,但无法传递给子组件,已尝试数小时未解决。

原代码问题分析

初始版本问题

  • 使用全局变量token存储accessToken,React组件不会监听全局变量变化,PowerBIClient挂载后不会因token更新重新渲染,始终拿到初始空值。
  • ProfileContent内部维护accessToken状态,与路由组件不在同一状态层级,无法直接共享。

更新版本问题

  • App组件给ProfileContent传参时,仅传递accessToken未传递setAccessToken,导致ProfileContent调用setAccessToken报错。
  • useEffect依赖数组为空,未加入instance和accounts,存在闭包问题,可能无法获取最新MSAL实例和用户账户。
  • setAccessToken后立刻打印accessToken,因setState是异步操作,此时打印的仍是旧值。

修正方案

  1. 状态提升与共享:将accessToken状态提升到App组件,确保路由组件和ProfileContent共享同一状态源,状态更新时所有依赖组件自动重新渲染。
  2. 正确传递状态更新函数:给ProfileContent传递setAccessToken,允许其更新App中的全局状态。
  3. 修复useEffect依赖:将instance和accounts加入useEffect依赖数组,避免闭包导致的旧值引用。
  4. 处理异步状态更新:通过useEffect监听accessToken变化实现打印,而非在setAccessToken后直接打印。
  5. TypeScript类型优化:给组件props定义明确类型,避免any类型,提升代码健壮性。

修正后的完整代码

App组件

import { useState, useEffect } from 'react';
import { Routes, Route, Link, useResolvedPath, useMatch } from 'react-router-dom';
import { MsalProvider, useMsal, AuthenticatedTemplate, UnauthenticatedTemplate } from '@azure/msal-react';
import { IPublicClientApplication } from '@azure/msal-browser';
import Oversikt from './Oversikt';
import Mineoppdrag from './Mineoppdrag';
import PowerBIClient from './PowerBIClient';
import Ansvarsområde from './Ansvarsområde';
import ProfileData from './ProfileData';
import ProfilePic from './ProfilePic';
import PageLayout from './PageLayout';

function ActiveLink({ to, children, ...props }: { to: string; children: React.ReactNode; [key: string]: any }) {
  const resultPath = useResolvedPath(to);
  const isActive = useMatch({ path: resultPath.pathname, end: true });
  return (
    <li className={isActive ? "active" : ""}>
      <Link to={to} {...props}>
        {children}
      </Link>
    </li>
  );
}

type AppProps = {
  pca: IPublicClientApplication
};

type ProfileContentProps = {
  accessToken: string;
  setAccessToken: React.Dispatch<React.SetStateAction<string>>;
};

function ProfileContent({ accessToken, setAccessToken }: ProfileContentProps) {
  const { instance, accounts } = useMsal();
  const [graphData, setGraphData] = useState<any | null>(null);

  const name = accounts[0]?.name?.split(',')[1];

  useEffect(() => {
    if (!accounts[0]) return;

    const request = {
      ...loginRequest,
      account: accounts[0]
    };

    instance.acquireTokenSilent(request)
      .then((response) => {
        callMsGraph(response.accessToken)
          .then(setGraphData)
          .catch((e) => console.log(e));
        
        if (response.accessToken) {
          setAccessToken(response.accessToken);
        }
      })
      .catch((e) => {
        console.log(e, "TokenPopUPError");
        instance.acquireTokenPopup(request)
          .then((response) => {
            callMsGraph(response.accessToken)
              .then(setGraphData);
            setAccessToken(response.accessToken);
          });
      });
  }, [instance, accounts, setAccessToken]);

  useEffect(() => {
    console.log(accessToken + "TOKEN");
  }, [accessToken]);

  return (
    <>
      <p className="card-title">Velkommen tilbake,</p>
      <h5 className="navBar-name">{name}</h5>
      <h5></h5>
      <Link to="/oversikt" className="test">Min oversikt</Link>
      <Link to="/oppdrag" className="test">Mine oppdrag</Link>
      <Link to="/ansvarsområde" className="test">Mitt ansvarsområde</Link>
      {graphData && accessToken ? (
        <div>
          <ProfileData graphData={graphData} />
          <ProfilePic accessToken={accessToken} />
        </div>
      ) : (
        <h5>Something bad happened, error!</h5>
      )}
    </>
  );
}

function App({ pca }: AppProps) {
  const [accessToken, setAccessToken] = useState<string>("");

  return (
    <div>
      <nav>
        <MsalProvider instance={pca}>
          <Routes>
            <Route path="/oversikt" element={<Oversikt />} />
            <Route path="/oppdrag/*" element={<Mineoppdrag />} />
            <Route path="/oppdrag/rapport" element={<PowerBIClient accessToken={accessToken} />} />
            <Route path="/ansvarsområde" element={<Ansvarsområde />} />
            <Route path="/login" element={<UnauthenticatedTemplate />} />
          </Routes>
          <PageLayout>
            <AuthenticatedTemplate>
              <div id="container">
                <ProfileContent accessToken={accessToken} setAccessToken={setAccessToken} />
              </div>
            </AuthenticatedTemplate>
            <UnauthenticatedTemplate>
              <div className="signinText">You are not signed in. Please sign in!</div>
            </UnauthenticatedTemplate>
          </PageLayout>
        </MsalProvider>
      </nav>
    </div>
  );
}

export default App;

PowerBIClient组件

import React from 'react';
import { PowerBIEmbed } from 'powerbi-client-react';
import * as models from 'powerbi-models';

interface PowerBIClientProps {
  accessToken: string;
}

declare global {
  interface Window {
    report: any;
  }
}

export default class PowerBIClient extends React.Component<PowerBIClientProps> {
  public render() {
    console.log(this.props.accessToken + "TOKEN");
    
    return (
      <PowerBIEmbed
        embedConfig={{
          type: 'report',
          embedUrl: "url",
          accessToken: this.props.accessToken,
          tokenType: models.TokenType.Aad,
          settings: {
            panes: {
              filters: {
                expanded: false,
                visible: true
              }
            },
          }
        }}
        eventHandlers={new Map([
          ['loaded', () => console.log('Report loaded')],
          ['rendered', () => console.log('Report rendered')],
          ['error', (event: any) => console.log(event.detail)]
        ])}
        cssClassName="Embed-container"
        getEmbeddedComponent={(embeddedReport) => {
          window.report = embeddedReport;
          console.log(this.props.accessToken + "TOKEN");
        }}
      />
    );
  }
}

关键说明

  • 状态提升到App组件后,accessToken的任何更新都会触发PowerBIClient重新渲染,确保拿到最新token。
  • useEffect依赖数组添加instance和accounts,保证用户账户或实例变化时重新获取token。
  • 明确的TypeScript类型定义避免了any类型带来的潜在问题,提升代码可维护性。

内容的提问来源于stack exchange,提问作者Alexander Brandhaug

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 10:40:25