1

I'm new on ReactJS. I follow thistutorial but I got error with my Router

There is my app.js

import './App.css';
import {BrowserRouter} from 'react-router-dom';
import MyRoutes from "./MyRoutes";
function App() {
  return (
    <BrowserRouter>
      <div className="App">
        <MyRoutes/>
      </div>
    </BrowserRouter>
 );
}
export default App;

My MyRoutes.js

import React from 'react';
import {Routes,Route,Navigate} from 'react-router-dom';
import Home from "./components/pages/HomeComponent";
import Login from "./components/pages/LoginComponent";
import Register from "./components/pages/RegisterComponent";
import PrivateRoute from './PrivateRoute';
import {Guard} from './Guard';
import Header from './components/layouts/Header';
function MyRoutes(){
  return (
    <>
        <Header/>
        <Routes>
            <Route exact path="/" render={props => (
                <Navigate to={{ pathname: '/home' }} />
            )}/>
            <Route path="/home" element={<Home/>}/>
            <Route path="/user/login" element={<Login/>}/>
            <Route path="/user/register" element={<Register/>}/>
            {/*Redirect if not authenticated */}
            <Route element={<Guard path="/user" token="user-token" routeRedirect="/user/login" element={<PrivateRoute/>} />} />
        </Routes>
    </>
);
}
export default MyRoutes;

My Guard.js

import React from 'react';
import {Route, Navigate, Outlet} from 'react-router-dom';
export const Guard = ({component:Component, token:Token, routeRedirect,...rest}) => (
<Route {...rest} render={props => (
    localStorage.getItem(Token) ?
        <Outlet/> :
        <Navigate to={{pathname:routeRedirect, state:{from:props.location}}} />
)}/>
);

And my PrivateRoute.js

import React from 'react';
import {Routes ,Route,Navigate} from 'react-router-dom';
import Profile from './components/pages/ProfileComponent';
export default function PrivateRoute(props) {
return (
    <div>
        {/*<Header/>*/}
        <Routes>
            <Route exact path={`${props.match.path}/view-profile`} element={<Profile/>}/>
            <Route exact path={props.match.path} render={props=> (
                <Navigate to={{ pathname: `${props.match.path}/view-profile` }} />
            )} />
        </Routes>
    </div>
);
}

And I got this error : " [Guard] is not a component. All component children of must be a or <React.Fragment>"

I do some research and read this post but I don't understand how to solve my issue

Could you help me please ?

I use React v17.0.2, react-router v6.2.2 and react-router-dom v6.2.2

Cohchi
  • 543
  • 3
  • 9
  • 19
  • What isn't clear about the error? `Guard` component isn't a `Route` or `React.Fragment` so it can't be rendered by a `Routes` component. It seems you are asking several questions: (1) how to upgrade from RRDv5 to RRDv6, (2) How to create a private route component, and (3) the error. Have you followed the [v5/v6 migration guide](https://reactrouter.com/docs/en/v6/upgrading/v5)? – Drew Reese Mar 14 '22 at 17:36
  • I understood this error but I don't know how to solve this. The migration guide doesn't explain how to do this – Cohchi Mar 14 '22 at 17:48
  • Please try following the upgrade guide to first resolve all the route issues first... then after those are fixed and you've *only* the route protection remaining we can help with that specific issue. In case it's not clear, the RRDv6 routes use only an `element` prop for their routed components. – Drew Reese Mar 14 '22 at 17:52
  • Thanks @DrewReese, I updated my post with new code (element prop) but I don't understand how to get routes from PrivateRoute and Guard components – Cohchi Mar 15 '22 at 19:56
  • Because only Guard check if token exist and then, get routes from PrivateRoute. Also with Guard we can prefix our routes (here, it's only "user" ("user/view-profile") but after, I can do this with new prefix like "store" and redirect ton another route – Cohchi Mar 15 '22 at 20:05
  • Okay so I updated my MyRoutes.js and Guard.js (look at my post) but I don't understand how to get Path in PrivateRoute – Cohchi Mar 15 '22 at 20:29

1 Answers1

1

Issue

The Guard component is still trying to directly render a Route component, which is invalid in react-router-dom@6. It just needs to render the Outlet or redirect.

All Route components use the element prop that takes a React.ReactNode, a.k.a. JSX. There are no longer any component or render and children function props.

Nest the one "/user/view-profile" route that PrivateRoute was rendering directly in the Guard layout route.

Example Solution

export const Guard = ({ token, routeRedirect }) => {
  const location = useLocation();

  return localStorage.getItem(token)
    ? <Outlet/>
    : <Navigate to={routeRedirect} replace state={{ from: location }} />;
};

Usage:

function MyRoutes() {
  return (
    <>
      <Header/>
      <Routes>
        <Route path="/" element={<Navigate to="/home" replace />} />
        <Route path="/home" element={<Home />}/>
        <Route path="/user/login" element={<Login />}/>
        <Route path="/user/register" element={<Register />}/>
        
        <Route
          element={<Guard token="user-token" routeRedirect="/user/login" />}
        >
          <Route path="/user/view-profile" element={<Profile />} />
        </Route>
        ... any other protected routes ...
      </Routes>
    </>
  );
}
Drew Reese
  • 165,259
  • 14
  • 153
  • 181