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>
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.
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;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With