这是我的index.tsx

import ReactDOM from 'react-dom/client';
import App from './App';
import { PublicClientApplication } from '@azure/msal-browser';
import { MsalProvider } from '@azure/msal-react';
import { msalConfig } from './azureAuthConfig';

const msalInstance = new PublicClientApplication(msalConfig);

const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);

root.render(
  <MsalProvider instance={msalInstance}>
    <App />
  </MsalProvider>
);

这是我的应用程序.tsx

import { BrowserRouter, Routes, Route } from "react-router-dom"
import './App.css';
import Layout from './components/Layout';
import Home from './components/Home';
import AuthRequired from './components/AuthRequired';
import Usage from "./components/Usage";
import Login from "./components/Login";

export default function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Layout />}>
          <Route index element={<Home />} />
          <Route element={<AuthRequired />} >
            <Route path="usage" element={<Usage />} />
          </Route>
          <Route path="/login" element={<Login />} />
        </Route>
      </Routes>
    </BrowserRouter >
  );
}

这是我的Layout组件,我在其中定义了一些Outlet上下文.

import React from 'react';
import Header from "./Header"
import Footer from "./Footer"
import { Outlet } from 'react-router-dom';
import { ReportingPeriod, SelectedProjects } from '../types';
import { DefaultReportingPeriod } from '../utils';

export default function Layout() {
  // Set Context for the Pollination key
  const [pollinationKey, setPollinationKey] = React.useState<string>("")

  // Set reporting period to be used on the rest of the app
  const [reportingPeriod, setReportingPeriod] = React.useState<ReportingPeriod>({
    start: DefaultReportingPeriod().start,
    end: DefaultReportingPeriod().end
  });

  // Set Project names to be used on the rest of the app
  const [selectedProjects, setSelectedProjects] = React.useState<SelectedProjects>({});

  return (
    <>
      <Header />
      <Outlet
        context={{
          pollinationKey, setPollinationKey,
          reportingPeriod, setReportingPeriod,
          selectedProjects, setSelectedProjects
        }}
      />
      <Footer />
    </>
  )
}

我看到我可以在Login路由上成功访问此上下文,但不能在Usage路由上成功访问,这是一条受保护的路由.为什么会这样呢?

下面是我try 访问其中一个上下文值时收到的错误

未捕获的TypeError:无法分析的属性""reportingPeriod"" ‘(0, React_router_dom__WEBPACK_IMPORTED_MODULE_5__.useOutletContext)(...)‘ 因为它是未定义的.

我try 了以下几种方法:

  1. 正如documentation所建议的,我确实在布局组件上创建了一个钩子,以便在Usage组件中使用.但没有奏效.结果是一样的.

  2. 我try 将Usage路由从受保护的路由中删除,只是为了进行测试.啊,真灵.我能够访问这些价值.

  3. 如果我使用React的Context API并使用如下所示的上下文提供程序包装BrowserRouter.有时灵

    export default function App() {
      return (
        <AppProvider>
          <BrowserRouter>
            <Routes>
              <Route path="/" element={<Layout />}>
                <Route index element={<Home />} />
                <Route element={<AuthRequired />} >
                  <Route path="usage" element={<Usage />} />
                </Route>
                <Route path="/login" element={<Login />} />
              </Route>
            </Routes>
          </BrowserRouter >
        </AppProvider>
      );
    }
    

如上所述,我非常希望使用Reaction路由V6的S插座上下文来实现此功能.

推荐答案

如果正常实现,LayoutAuthRequired都应该呈现Outlets个组件.问题似乎是,您正在通过useOutletContext直接在叶路由组件中解构reportingPeriod,例如Usage.useOutletContext挂钩访问最接近的祖先Outlet组件的上下文值,例如由AuthRequired提供的组件.

您可以更新AuthRequired以访问任何祖先Outlet上下文并沿ReactTree向下转发.

示例:

export default function Layout() {
  ...

  return (
    <>
      <Header />
      <Outlet
        context={{
          pollinationKey,
          setPollinationKey,
          reportingPeriod,
          setReportingPeriod,
          selectedProjects,
          setSelectedProjects
        }}
      />
      <Footer />
    </>
  )
}
const AuthRequired = () => {
  const context = useOutletContext(); // <-- any ancestor context value

  ... AuthRequired business logic ...

  return <someCondition>
    ? (
      <Outlet
        context={{
          ...context, // <-- shallow copy ancestor context value
          // add any new context value
        }}
      />
    ) : <Navigate to="/login" replace />;
};
export default function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Layout />}>
          <Route index element={<Home />} />
          <Route
            element={<AuthRequired />} // <-- consumes Layout outlet context 
          >
            <Route
              path="usage"
              element={<Usage />} // <-- consumes AuthRequired outlet context 
            />
          </Route>
          <Route path="/login" element={<Login />} />
        </Route>
      </Routes>
    </BrowserRouter >
  );
}
const Usage = () => {
  const { reportingPeriod } = useOutletContext();

  ...
};

Typescript相关问答推荐

Angular 过滤器形式基于某些条件的数组控制

TypScript ' NoInfer '类型未按预期工作

TypeScript类型不匹配:在返回类型中处理已经声明的Promise Wrapper

如何缩小变量访问的对象属性范围?

rxjs forkJoin在错误后不会停止后续请求

创建一个根据布尔参数返回类型的异步函数

具有泛型类型和缺省值的键

有条件地删除区分的联合类型中的属性的可选属性

Vue3 Uncaught SyntaxError:每当我重新加载页面时,浏览器控制台中会出现无效或意外的令牌

如何将TS泛型用于react-hook-form接口?

编译TypeScrip项目的一部分导致错误

如何键入派生类的实例方法,以便它调用另一个实例方法?

如何在打字角react 式中补齐表格组

有没有一种更好的方法来存储内存中的数据,而不是在类型脚本中使用数组和基于索引的函数?

有没有什么方法可以使用ProvideState()提供多个削减器作为根减减器

可以用任意类型实例化,该类型可能与

使用泛型以自引用方式静态键入记录的键

元组解释

在Nestjs中,向模块提供抽象类无法正常工作

如何编写一个接受 (string|number|null|undefined) 但只返回 string 且可能返回 null|undefined 的函数?