【问题标题】:Error: [PrivateRoute] is not a <Route> component. All component children of <Routes> must be a <Route> or <React.Fragment>错误:[PrivateRoute] 不是 <Route> 组件。 <Routes> 的所有子组件必须是 <Route> 或 <React.Fragment>
【发布时间】:2021-12-20 03:40:49
【问题描述】:

我正在使用 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

在文件 route.js 我写成:

 ...
<PrivateRoute exact path="/" element={<Dashboard/>}/>
<Route exact path="/home" element={<Home/>}/>

我也经历过同样的例子React-router Auth Example - StackBlitz, file App.tsx

我有什么遗漏吗?

【问题讨论】:

    标签: javascript reactjs react-router-dom


    【解决方案1】:

    我今天遇到了同样的问题,并根据very helpful articleAndrew 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" />;
    }
    

    在 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>
        
      );
    }
    

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

    <Route exact path='/' element={<PrivateRoute/>}>
          <Route exact path='/' element={<Home/>}/>
    </Route>
    

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

    【讨论】:

    • 啊,我读了那个博客,现在简单地将&lt;Route element={&lt;Home/&gt;} /&gt; 渲染为私人出口的孩子更有意义。这现在有点吸引人了。我可以看到某些用例的好处。
    • 如果使用最新版本,则不需要exact属性。
    • @DrewReese 是否有某种内置的 Auth 组件?我不是 React 方面的专家,所以我很想看看其他解决方案。据我了解,此示例中的 PrivateRoute 组件充当 Auth 组件
    • @DrewReese 刚刚意识到您说的是路线内的附加路线。如果主页是唯一需要授权的页面,那么您是对的,子路由不是必需的。如果多个路由需要相同的身份验证,那么将它们都放在同一个 PrivateRoute 中很方便。
    • @DallinRomney 在 React 中,不,当涉及到身份验证实现时,仅 React 是相当无主见的,但任何使用的库都可以提供自己的身份验证容器/包装器。这就是吸引人的部分。 :) 这实际上与我在 v5 中使用的模式相同,并且我称之为“金门”路线,它是进入“围墙花园”路线的单一身份验证点,现在每个人都不需要身份验证检查。
    【解决方案2】:

    只有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>
      }
    />
    

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

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

    并使用

    <Route
      path="/dashboard"
      element={
        <PrivateRoute>
          <Dashboard />
        </PrivateRoute>
      }
    />
    

    【讨论】:

    • Dallin 的回答很好,但老实说,仅仅用身份验证组件包装目标组件并没有为您节省太多东西。如果有的话,它是一个更复杂的解决方案,因为它现在涉及渲染 两个 Route 组件 一个 Outlet只是为了渲染一条路径。
    • 谢谢,这是我的解决方案,它奏效了。
    • @DrewReese,我认为当您有许多需要保护的路线时,Dallin 的答案的优势就会发挥作用。所以只有一条受保护的路线,是的,这没有任何意义,但是 5 或 20 条呢?它开始节省不需要一堆多余的包装器组件。
    • @MikeyT 完全同意,请参阅他的回答下的 cmets。我的最初基本上是直接取自官方文档,用于保护单个组件。在解决这类问题时,我通常会同时涵盖这两个用例。
    【解决方案3】:

    只需将你的路由组件设置为 element prop:

    <Routes>
      <Route exact path="/" element={<Home />} />
      <Route path="/about" element={<About />} />
      <Route path="/dashboard" element={<Dashboard />} />
    </Routes>
    

    您还可以检查是否从 v5 升级, https://reactrouter.com/docs/en/v6/upgrading/v5

    【讨论】:

      【解决方案4】:

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

          <Route path="/protected" element={<RequireAuth><ProtectedPage /></RequireAuth>} />
      
      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;
      };
      

      如果你需要的话,你应该在 ProtectedPage 内部添加更多路由。

      有关详细信息,请参阅 docsexample。另外,请查看 Michael Jackson 的 this note,了解一些实施细节。

      【讨论】:

        【解决方案5】:

        React Router v6,一些语法糖:

        {auth && (
          privateRoutes.map(route =>
            <Route
              path={route.path}
              key={route.path}
              element={auth.isAuthenticated ? <route.component /> : <Navigate to={ROUTE_WELCOME_PAGE} replace />}
            />
          )
        )}
        

        【讨论】:

        • 语法糖用什么方式?它是如何解决问题的?一个解释将是有序的。例如,想法/要点是什么?来自the Help Center“...总是解释为什么你提出的解决方案是合适的以及它是如何工作的”。请通过editing (changing) your answer 回复,而不是在 cmets 中(without "Edit:"、"Update:" 或类似的 - 答案应该看起来像是今天写的)。
        【解决方案6】:

        对于较长的元素

                <Router>
                <div>
                    <Navbar totalItems={cart.total_items}/>
                    <Routes>
                        <Route exact path='/'>
                            <Route exact path='/' element={<Products products={products} onAddToCart={handleAddToCart}/>}/>
                        </Route>
                        <Route exact path='/cart'>
                            <Route exact path='/cart' element={<Cart cart={cart}/>}/>     
                        </Route>
                    </Routes>
                </div>
            </Router>
        

        【讨论】:

        • 解释一下。例如,想法/要点是什么?来自the Help Center“...总是解释为什么你提出的解决方案是合适的以及它是如何工作的”。请通过editing (changing) your answer 回复,而不是在 cmets 中(without "Edit:"、"Update:" 或类似的 - 答案应该看起来像是今天写的)。
        【解决方案7】:

        标题将保留在所有页面上

        import React from 'react';
        
        import {
          BrowserRouter,
          Routes,
          Route
        } from "react-router-dom";
        
        const Header = () => <h2>Header</h2>
        const Dashboard = () => <h2>Dashboard</h2>
        const SurveyNew = () => <h2>SurveyNew</h2>
        const Landing = () => <h2>Landing</h2>
        
        
        const App = () =>{
          return (
            <div>
              <BrowserRouter>
                <Header />
                <Routes >
                <Route exact path="/" element={<Landing />} />
                <Route path="/surveys" element={<Dashboard />}  />
                <Route path="/surveys/new" element={<SurveyNew/>}  />
                </Routes>
              </BrowserRouter>
            </div>
          );
        };
        export default App;
        

        【讨论】:

        • 解释一下。例如,想法/要点是什么? “标题将保留在所有页面上”是什么意思?您的意思是“标题将保留在所有页面上”?或者是其他东西?来自the Help Center“...总是解释为什么你提出的解决方案是合适的以及它是如何工作的”。请通过editing (changing) your answer 回复,而不是在 cmets 中(without "Edit:"、"Update:" 或类似的 - 答案应该看起来像是今天写的)。
        • 文本标题固定在所有页面上,您可以放置​​导航栏等。
        【解决方案8】:
        import { BrowserRouter as Router, Routes, Route, Link } from "react-router-dom";
        
        function App() {
          return (
              <Router>
                  <Routes>
                    <Route path="/" element={<h1>home page</h1>} />
                    <Route path="/seacrch" element={<h1>seacrch page</h1>} />
                  </Routes>
              </Router>
          );
        }
        
        export default App;
        

        【讨论】:

        • 正如目前所写,您的答案尚不清楚。请edit 添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。你可以找到更多关于如何写好答案的信息in the help center
        • 解释一下。例如,想法/要点是什么?来自the Help Center“...总是解释为什么你提出的解决方案是合适的以及它是如何工作的”。请通过editing (changing) your answer 回复,而不是在 cmets 中(without "Edit:"、"Update:" 或类似的 - 答案应该看起来好像是今天写的)。
        猜你喜欢
        • 1970-01-01
        • 2021-12-30
        • 2022-01-22
        • 1970-01-01
        • 2022-01-01
        • 2022-01-01
        • 2022-01-07
        • 2022-11-16
        相关资源
        最近更新 更多