反应路由器上下文未到达受保护的路由

问题描述 投票:0回答:1

这是我的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 >
  );
}

这是我的布局组件,我在其中定义了一些出口上下文

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 />
        </>
    )
}

我发现我可以在登录路由上成功访问此上下文,但不能在受保护的使用路由上成功访问此上下文。为什么会这样?

这是我尝试访问其中一个上下文值时遇到的错误

Uncaught TypeError: Cannot destructure property 'reportingPeriod' of '(0 , react_router_dom__WEBPACK_IMPORTED_MODULE_5__.useOutletContext)(...)' as it is undefined.

我尝试了以下方法:

  1. 根据documentation的建议,我确实在布局组件上创建了一个钩子以在使用组件中使用。没用。结果相同。

  2. 我尝试将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>
  );
}

如上所述,我真的很想使用React Router V6的outletContext来实现这个功能。

reactjs typescript react-router react-router-dom
1个回答
0
投票
如果正常实现,

Layout

AuthRequired
都应该渲染
Outlets
组件。问题似乎是您直接在叶路由组件中通过 
reportingPeriod
 解构 
useOutletContext
,例如
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 />;
};
const Usage = () => {
  const { reportingPeriod } = useOutletContext();

  ...
};
    
© www.soinside.com 2019 - 2024. All rights reserved.