Home > OS >  Error: [PrivateRoute] is not a <Route> component. All component children of <Routes> mus
Error: [PrivateRoute] is not a <Route> component. All component children of <Routes> mus

Time:11-07

I'm using react router v6 and creating private routes for my application.

In PrivateRoute.js, i've code

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

and in route.js i've written as

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

I've gone through same example https://stackblitz.com/github/remix-run/react-router/tree/main/examples/auth?file=src/App.tsx

Is there any something i'm missing thank you.

CodePudding user response:

I ran into the same issue today and came up with the following solution based on this very helpful article by Andrew Luca

In 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" />;
}

In App.js (I've left in some other pages as examples):

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

In the above routing, this is the private route:

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

If authorization is successful, the element will show. Otherwise, it will navigate to the login page.

CodePudding user response:

You've passed element={<Dashboard/>} and in doing so you actually invoked the constructor and created that component, that's your first mistake. You want to do element={Dashboard}.

The second thing is that when you create a private route you don't ever again use Route, you directly render the component that you've passed to it, the PrivateRoute is a route already. You want a pattern like this:

const AuthRoute = ({Component, ...otherProps) => {
  const auth = // check auth

  return auth ? <Component/> : <Redirect to='/login'/>
}

That's not exactly correct but it's to show you how you have to pass the main component. If you're using typescript you can also use RouteComponentProps on the auth route to emphasize that it is a route itself.

In your case specifically the error that you're getting is because of element={ele} and the already constructed component you've passed with element={<Dashboard/>}.

CodePudding user response:

Only Route components can be a child of Routes. If you follow the v6 docs then you'll see the authentication pattern is to use a wrapper component to handle the authentication check and redirect.

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

The old v5 pattern of create custom Route components no longer works. An updated v6 pattern using your code/logic could look as follows:

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

And to use

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