错误:[PrivateRoute] 不是 <Route> 组件。<Routes> 的所有子组件都必须是 <Route> 或 <React.Fragment>

Raj*_*boy 207 javascript reactjs react-router-dom

我正在使用 React Router v6 并为我的应用程序创建私有路由。

在文件PrivateRoute.js中,我有代码

import React from 'react';
import {Route,Navigate} from "react-router-dom";
import {isauth}  from 'auth'

function PrivateRoute({ element, path }) {
  const authed = isauth() // isauth() returns true or false based on localStorage
  const ele = authed === true ? element : <Navigate to="/Home"  />;
  return <Route path={path} element={ele} />;
}

export default PrivateRoute
Run Code Online (Sandbox Code Playgroud)

在文件route.js中我写的是:

 ...
<PrivateRoute exact path="/" element={<Dashboard/>}/>
<Route exact path="/home" element={<Home/>}/>
Run Code Online (Sandbox Code Playgroud)

我已经完成了相同的示例React-router Auth 示例 - StackBlitz,文件 App.tsx

我有什么遗漏的吗?

Dal*_*ney 229

我今天遇到了同样的问题,并根据Andrew Luca的这篇非常有用的文章提出了以下解决方案

在 PrivateRoute.js 中:

import React from 'react';
import { Navigate, Outlet } from 'react-router-dom';

const PrivateRoute = () => {
    const auth = null; // determine if authorized, from context or however you're doing it

    // If authorized, return an outlet that will render child elements
    // If not, return element that will navigate to login page
    return auth ? <Outlet /> : <Navigate to="/login" />;
}
Run Code Online (Sandbox Code Playgroud)

在 App.js 中(我在其他一些页面中留下了示例):

import './App.css';
import React, {Fragment} from 'react';
import {BrowserRouter as Router, Route, Routes} from 'react-router-dom';
import Navbar from './components/layout/Navbar';
import Home from './components/pages/Home';
import Register from './components/auth/Register'
import Login from './components/auth/Login';
import PrivateRoute from './components/routing/PrivateRoute';

const App = () => {
  return (
    <Router>
      <Fragment>
        <Navbar/>
        <Routes>
          <Route exact path='/' element={<PrivateRoute/>}>
            <Route exact path='/' element={<Home/>}/>
          </Route>
          <Route exact path='/register' element={<Register/>}/>
          <Route exact path='/login' element={<Login/>}/>
        </Routes>
      </Fragment>
    </Router>
    
  );
}
Run Code Online (Sandbox Code Playgroud)

在上面的路由中,这是私有路由:

<Route exact path='/' element={<PrivateRoute/>}>
      <Route exact path='/' element={<Home/>}/>
</Route>
Run Code Online (Sandbox Code Playgroud)

如果授权成功,将显示该元素。否则,它将导航到登录页面。

  • 如果使用最新版本,则不需要“exact”属性。 (20认同)
  • 啊,我读了那个博客,现在简单地将 `&lt;Route element={&lt;Home/&gt;} /&gt;` 渲染为私有插座的子项更有意义。现在这更有吸引力了。我可以看到某些用例的好处。 (4认同)
  • @DallinRomney 在 React 中,不,在 auth 实现方面,React 本身相当不固执,但是使用的任何库当然都可以提供自己的 auth 容器/包装器。这就是吸引人的部分。:) 这实际上与我在 v5 中使用的模式相同,我称之为“金门”路由,它是进入“围墙花园”路由的单一身份验证点,每个路由现在都不需要身份验证检查。 (2认同)

Dre*_*ese 77

只有Route组件可以是 的子级Routes。如果您遵循 v6 文档,那么您将看到身份验证模式是使用包装器组件来处理身份验证检查和重定向。

function RequireAuth({ children }: { children: JSX.Element }) {
  let auth = useAuth();
  let location = useLocation();

  if (!auth.user) {
    // Redirect them to the /login page, but save the current location they were
    // trying to go to when they were redirected. This allows us to send them
    // along to that page after they login, which is a nicer user experience
    // than dropping them off on the home page.
    return <Navigate to="/login" state={{ from: location }} />;
  }

  return children;
}

...

<Route
  path="/protected"
  element={
    <RequireAuth>
      <ProtectedPage />
    </RequireAuth>
  }
/>
Run Code Online (Sandbox Code Playgroud)

创建自定义组件的旧 v5 模式Route不再有效。使用您的代码/逻辑更新的 v6 模式可能如下所示:

const PrivateRoute = ({ children }) => {
  const authed = isauth() // isauth() returns true or false based on localStorage
  
  return authed ? children : <Navigate to="/Home" />;
}
Run Code Online (Sandbox Code Playgroud)

并使用

<Route
  path="/dashboard"
  element={
    <PrivateRoute>
      <Dashboard />
    </PrivateRoute>
  }
/>
Run Code Online (Sandbox Code Playgroud)

  • Dallin 的答案很好,但说实话,与仅用身份验证组件包装目标组件相比,并没有为您节省太多东西。如果有的话,这是一个更复杂的解决方案,因为它现在涉及渲染***两个***“Route”组件***和***一个“Outlet”,只是为了渲染单个路径。 (7认同)
  • 谢谢这是我的解决方案并且有效。 (2认同)

Dhe*_*ean 44

补充减少代码行数,使其更具可读性和美观性。

这可能只是一条评论,但我没有足够的观点,所以我将其作为答案。

达林的答案有效,但德鲁的答案更好!为了完成德鲁关于美学的回答,我建议创建一个私有组件,将组件作为道具而不是子组件。

私有路由文件/组件的非常基本的示例:

import { Navigate } from 'react-router-dom';

const Private = (Component) => {
    const auth = false; //your logic

    return auth ? <Component /> : <Navigate to="/login" />
}
Run Code Online (Sandbox Code Playgroud)

路由文件示例:

<Routes>
    <Route path="/home" element={<Home />} />
    <Route path="/user" element={<Private Component={User} />} />
</Routes>
Run Code Online (Sandbox Code Playgroud)

  • 它对我有用,我只需将 `const Private = (Component) =&gt;` 更改为 const `Private = ({Component}) =&gt;` (11认同)

JLa*_*rky 10

我知道这并不完全是如何工作的秘诀PirvateRoute,但我只是想提一下,新文档建议使用稍微不同的方法来使用react-router v6处理这种模式:

<Route path="/protected" element={<RequireAuth><ProtectedPage /></RequireAuth>} />
Run Code Online (Sandbox Code Playgroud)
import { Navigate, useLocation } from "react-router";

export const RequireAuth: React.FC<{ children: JSX.Element }> = ({ children }) => {
  let auth = useAuth();
  let location = useLocation();

  if (!auth.user) {
    return <Navigate to="/login" state={{ from: location }} />;
  }

  return children;
};
Run Code Online (Sandbox Code Playgroud)

ProtectedPage如果需要,您应该在其内部添加更多路由。

有关更多详细信息,请参阅文档示例。另外,请查看Michael Jackson 的这篇注释,其中介绍了一些实现细节。


fil*_*der 7

现在是 2022 年,我做了如下的事情:

// routes.tsx

import { lazy } from "react";
import { Routes, Route } from "react-router-dom";
import Private from "./Private";
import Public from "./Public";

const Home = lazy(() => import("../pages/Home/Home"));
const Signin = lazy(() => import("../pages/Signin/Signin"));

export const Router = () => {
  return (
    <Routes>
      <Route path="/" element={Private(<Home />)} />
      <Route path="/signin" element={Public(<Signin />)} />
    </Routes>
  );
};
Run Code Online (Sandbox Code Playgroud)
// Private.tsx

import { Navigate } from "react-router-dom";
import { useEffect, useState } from "react";

function render(c: JSX.Element) {
  return c;
}

const Private = (Component: JSX.Element) => {
  const [hasSession, setHasSession] = useState<boolean>(false);

  useEffect(() => {
    (async function () {
      const sessionStatus = await checkLoginSession();

      setHasSession(Boolean(sessionStatus));
    })();
  }, [hasSession, Component]);


  return hasSession ? render(Component) : <Navigate to="signin" />;
};

export default Private;

Run Code Online (Sandbox Code Playgroud)

希望这可以帮助!

  • 您是部分正确的,如果我们想为经过身份验证的用户提供重定向,则需要 Public() 渲染器。如果“A”用户已经登录并且存在经过身份验证的会话,则显示“登录”或“注册”等公共页面会非常尴尬。 (2认同)