Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React V6 route guarding and routing within component

Im trying to use React Routing V6 for my project.

Currently im struggeling to make the authentication and routing itself to work. the idea of my code is:

Not authenticated user:

redirect to /login with my login component. (only login component)

Authenticated user:

Load the gameComponent component, and the rest of links inside of gamecomponent, will load inside gameComponents div class named middleContentHolder

examples:

authenticated user:

visits url /crime -> loads gamecomponent, and within gamecomponent it loads the crime component. visits url /test -> loads gamecomponent , and within gamecomponent it loads the SideBarRight component.

not authenticated user:

vitits url /crime -> not authenticated -> redirects to /login -> loads loginmodule only.

please note that in gamecomponent component, i do have links that will load within gamecomponent. app.js will either load the gamecomponent, or redirect user to login if not auth.

app.js:

import React from 'react';
import logo from './logo.svg';
import './App.css';
import GameComponent from './gameComponent.jsx';
import { BrowserRouter as Router } from 'react-router-dom';
import { Routes, Route, Navigate, Outlet } from 'react-router-dom';
import Crime from './components/game/crime.jsx';

import PrivateRoute from './PrivateRoute';
import Login from './components/login/login.jsx';




function App() {
  return (
  
  <Router>
    <Routes>
         <Route path="/" element={<GameComponent />}>
             <PrivateRoute isAuth={true} path="crime" component={Crime}  redirectTo='/login'/>
         </Route>
     </Routes>
  </Router>
  );
}

export default App;

Gamecomponent:

import React, {Component} from 'react';
//import Component from 'react-dom';
import SideBarRight from './components/game/sideBarRight.jsx';
import SideBarLeft from './components/game/sideBarLeft.jsx';
import Crime from './components/game/crime.jsx';
import Login from './components/login/login.jsx';
import './gameComponent.css';
import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom';
import { BrowserRouter} from "react-router-dom";


class GameComponent extends Component{
 constructor() {
    super();
    this.state = {
    userData: {
        user: {cash:0, bank:0, weapon:'', username: 'test', locationname: 'Bankok',
        defence: 0},
        rankbar: {rankpercent: 50, rank: 'Mafia'},
        }
    }
    
    
    
    
  }


render() {
    return (
    <div className="main">
    <div className="sidebar left">
                <SideBarLeft/>

    </div>
    <div className="middleContentHolder">
        <Route path="/" element={<Crime />} />

    <Route path="/test" element={<Crime />} />
    <Route path="/crime" element={<Crime />} />
    <Route path="/test" element={<SideBarRight UserData={this.state.userData} />} />

        <div className="col-8">
            <div className="content">
            <div className="header"><span>Test...</span></div>
            
            </div>
        </div>
    </div>
    <div className="sidebar right">
        <SideBarRight UserData={this.state.userData}/>
        </div>
    </div>
    );
  }
    
}



export default GameComponent;

PrivateRoute:(auth is just a dummy atm)

import React from 'react';
import PropTypes from 'prop-types';
import { Route, Navigate } from 'react-router-dom';
import { useNavigate  } from "react-router-dom";
import Login from './components/login/login.jsx';
import GameComponent from './gameComponent.jsx';


const PrivateRoute = ({ component: Component, redirectTo, isAuth, path, ...props }) => {
    isAuth = false;
    
    if(!isAuth) {
        return <Navigate to={redirectTo} />;
    }
    return <Route path={path} element={<Component />} />
};

export default PrivateRoute;

update:

orginal auth was:in privateroute:

isAuth = isAuth;

one example of non-working code that would show what i want:

<Route path="/login" element={}>

     <PrivateRoute isAuth={true} path="/" component={GameComponent}  redirectTo='/login'>
              rest of routes exist in gamecomponent..
     </PrivateRoute>
like image 871
Anetellie Avatar asked Oct 17 '25 15:10

Anetellie


2 Answers

With the official release of React Router V6, the other answer is no longer valid. It will throw an error since <PrivateRoute /> isn't a <Route />.

The proper way to do it is to refactor your <PrivateRoute /> component like so...

import { Navigate, useLocation } from "react-router-dom"

const PrivateRoute = (props: { children: React.ReactNode }): JSX.Element => {
  const { children } = props

  // Up to you specific app to decide how to get this value
  const isLoggedIn: boolean = localStorage.getItem('logged_user') !== null;
  const location = useLocation()

  return isLoggedIn ? (
    <>{children}</>
  ) : (
    <Navigate
      replace={true}
      to="/login"
      state={{ from: `${location.pathname}${location.search}` }}
    />
  )
}

Then whichever file you setup your routes in, you would do...

<Routes>
   <Route path="/PRIVATE" element={<PrivateRoute> <PrivatePage /> </PrivateRoute>}/>
   <Route path="/profile" element={<PrivateRoute> <ProfilePage /> </PrivateRoute>}/>
   <Route path="/login" element={<LoginPage />}/>
   <Route path="/" element={<HomePage />}/>
</Routes>

This is the proper way of doing it in V6 since only a <Route /> can be nested in a <Routes />. Then your authenticated logic gets moved into the element prop.

As an added bonus, the state={{ from: `${location.pathname}${location.search}` }} in PrivateRoute allows you to get the URL of the page they tried to enter, but was denied. This is passed to your login page, where you can redirect them back to the URL after they authenticate.

like image 182
Mr_Antivius Avatar answered Oct 19 '25 12:10

Mr_Antivius


Solution for newer version

Create custom middleware <AuthGuard> and use as wrapper

<PrivateRoute> not vaild for newer version as child of <Routes>

Error "A <Route> is only ever to be used as the child of <Routes> element"

App.js

import React from 'react'
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
import AuthGuard from "./Routes/AuthGuard";

function App() {
  return (
    <div className='App'>
        <Router>
          <Routes>
              <Route path='/' element={<Home />} />
              <Route path='/contact' element={<Contact />} />
              <Route path='/guest-page' element={<AuthGuard isGuest={true}><h1>Guest Page</h1></AuthGuard>} />
              <Route path='/protected-page' element={<AuthGuard requireToken={true}><h1>ProtectedPage</h1></AuthGuard>} />
          </Routes>
        </Router>
    </div>
  );
}

export default App;

AuthGuard.js

import { Route, useNavigate } from "react-router-dom";
import { useLayoutEffect } from "react";

const ProtectedRoute = ({requireToken, guest, children, ...rest}) => {
    
    console.log(requireToken, guest, children, rest); 
    
    const navigate = useNavigate();
    const hasToken = false;

    let hasAccess = (!requireToken || (requireToken && hasToken));
    let navigateTo = '/?login-rquired=true'; 

    if(guest) {
        hasAccess = !hasToken; 
        navigateTo = '/?guest=true';
        console.log("Guest > hasAccess: " + hasAccess)
    }

    if(requireToken){
        console.log("requireToken", requireToken)
    }
    
    useLayoutEffect(()=>{
        if (!hasAccess) {
            console.log("Not allowed");
            navigate(navigateTo);
        }
    },[])

    return (
        <>{ hasAccess ? children : "Login required" }</>
    )
}

export default ProtectedRoute;
like image 35
GMKHussain Avatar answered Oct 19 '25 12:10

GMKHussain