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>
If you only want GameComponent to load if use is authenticated, you will need to change your App component like this:
function App() {
return (
<Router>
<Routes>
<Route path="/login" element={<LoginComponent />} />
<PrivateRoute isAuth={true} path="/" component={GameComponent} redirectTo='/login'/>
</Routes>
</Router>
);
}
Here we are essentially putting a switch so that we can navigate to /login when there is no authentication. <Routes> is vital here, because it will only render the component that matches the exact path.
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.
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;
Related
i need help to protect routes in my reactjs project , i want that only a loged user can access to Home Page, and only not loged user can access to the signin and signup forms
import "./App.css";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import Navbar from "./components/Navbar";
import SignInForm from "./pages/SignInForm";
import SignUpForm from "./pages/SignUpForm";
import Wallet from "./components/Wallet";
import Welcome from "./pages/Welcome";
import Home from "./pages/Home";
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Welcome></Welcome>}></Route>
<Route path="/signin" element={<Navbar></Navbar>}>
<Route index element={<SignInForm></SignInForm>}></Route>
</Route>
<Route path="/signup" element={<Navbar></Navbar>}>
<Route index element={<SignUpForm></SignUpForm>}></Route>
</Route>
<Route path="/wallet" element={<Navbar></Navbar>}>
<Route index element={<Wallet></Wallet>}></Route>
</Route>
<Route path="/home" element={<Navbar></Navbar>}>
<Route index element={<Home></Home>}></Route>
</Route>
</Routes>
</BrowserRouter>
);
}
export default App;
This is how I did it, basically, you wrap the route and check if the user is authenticated, if he's not, redirect back to where he should be.
This uses react-router v6, you can find more information about it in the authentication section of the official docs. They have a very good example on stackblitz, you should take a look at that.
import { useIsAuthenticated } from "your-auth-package"
import { Navigate, useLocation } from "react-router";
const AuthenticatedRoute = ({ children }) => {
const isAuthenticated = useIsAuthenticated();
const location = useLocation();
// not logged in
if (!isAuthenticated) {
console.log("not authenticated");
return <Navigate to="/" state={{ from: location }} replace />;
}
return children;
};
<BrowserRouter>
<Routes>
<Route
path="/dashboard"
element={
<AuthenticatedRoute>
<Dashboard />
</AuthenticatedRoute>
}
/>
</Routes>
</BrowserRouter>
Make a component like below and adjust it to your requirement.
import React from "react";
import { Redirect, Route } from "react-router-dom";
import auth from "../services/authService";
function ProtectedRoute({ path, component: Component, render, ...rest }) {
return (
<Route
{...rest}
render={(props) => {
if (!auth.getCurrentUser())
return (
<Redirect
to={{
pathname: "/login",
state: { from: props.location },
}}
/>
);
return Component ? <Component {...props} /> : render(props);
}}
/>
);
}
export default ProtectedRoute;
then wrap the route like this
<ProtectedRoute path="/wallet" element={<Navbar></Navbar>}/>
I'm having those troubles about the private route and not being redirected. I'd like to know how to fix this code, please.
This is my App.js
import React from 'react'
import ReactDOM from 'react-dom'
import { BrowserRouter as Router, Switch, Routes, Route, Swit } from 'react-router-dom';
import MasterLayout from '../src/layouts/admin/MasterLayout'
import { Navigate } from 'react-router-dom';
import Dashboard from './components/admin/Dashboard';
import Profile from './components/admin/Profiles';
import Login from './components/frontend/frontend/Auth/Login';
import Register from './components/frontend/frontend/Auth/Register';
import Home from './components/frontend/Home';
import AdminPrivateRoute from './AdminPrivateRoute';
import axios from 'axios';
axios.defaults.baseURL = 'http://localhost:8000';
axios.defaults.headers.post['Content-Type'] = 'application/json';
axios.defaults.headers.post['Accept'] = 'application/json';
axios.defaults.withCredentials = true;
axios.interceptors.request.use(function (config) {
const token = localStorage.getItem('auth_token');
config.headers.Authorization = token ? `Bearer ${token}` : ''
return config;
}
)
function App() {
return (
<div className="App">
<Router>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/login" element={localStorage.getItem('auth_token') ? <Navigate to="/" /> : <Login />} >
</Route>
<Route path="/register" element={localStorage.getItem('auth_token') ? <Navigate to="/" /> : <Register />} >
</Route>
<Route path="/admin/*" element={<AdminPrivateRoute />} >
</Route>
</Routes>
</Router>
</div>
);
}
export default App;
This is my AdminPrivateRoute.js file
import React from 'react'
import { BrowserRouter as Router, Switch, Routes, Route, Swit } from 'react-router-dom';
import MasterLayout from './layouts/admin/MasterLayout';
import { Navigate,Outlet } from 'react-router-dom';
function AdminPrivateRoute({ ...rest }) {
return (
<Routes>
<Route {...rest}
render={({ props, location }) =>
localStorage.getItem('auth_token') ?
(<MasterLayout {...props} />) :
(<Navigate to={{ pathname: '/login', state: { from: location } }} />)
}
/>
</Routes>
);
}
export default AdminPrivateRoute;
This is my route.js
import Dashboard from '../components/admin/Dashboard';
import Profile from '../components/admin/Profiles';
import Login from '../components/frontend/frontend/Auth/Login';
import Register from '../components/frontend/frontend/Auth/Register';
const routes = [
{path:'/admin',exact:true,name:'Admin'},
{path:'/admin/dashboard',exact:true,name:'Dashboard',component: Dashboard},
{path:'/admin/profile',exact:true,name:'Profile',component: Profile},
{path:'/admin/login',exact:true,name:'Login',component: Login},
{path:'/admin/register',exact:true,name:'Register',component: Register},
];
export default routes;
I'm following a tutorial about React ( older version) and I'm trying to change to React V6.
Finally, What for is ...rest, render, locations, state from location? Can someone explain that block of code, in general, to understand what is happening.
Can you guide me, please?
Your AdminPrivateRoute component is certainly mixing up the older react-router-dom v5 syntax. In RRDv6 private "routes" are written to be wrapper components that render an Outlet (or children if wrapping a single component) or the Navigate component for redirection.
import { Navigate, Outlet, useLocation } from 'react-router-dom';
function AdminPrivateRoute() {
const location = useLocation();
return localStorage.getItem('auth_token')
? <Outlet /> // <-- nested routes rendered here
: <Navigate to="/login" replace state={{ from: location }} />;
}
Usage:
<Router>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/login" element={localStorage.getItem('auth_token') ? <Navigate to="/" /> : <Login />} />
<Route path="/register" element={localStorage.getItem('auth_token') ? <Navigate to="/" /> : <Register />} />
<Route element={<AdminPrivateRoute />} >
{routes.map(({ path, component: Component }) => (
<Route key={path} path={path} element={<Component />} />
))}
</Route>
</Routes>
</Router>
Note that the AdminPrivateRoute no longer spreads the rest of the passed props into a rest prop since passing props is no longer necessary. Similarly, the render prop is gone and we don't need to render a Route with the auth wrapper. Lastly, the location is the current location the user is attempting to access. The location is passed in route state to the authentication route so upon successful authentication the user can be redirected back to the original route they were trying access.
I want authenticated routes if user is not logged in the page should not be accessible like if someone enters in the url localhost.../admin/dashboard he should not be able to navigate instead he should be taken to signin page if not logged in.
I'm using react-router v6 and creating private routes for my application.
AdminRoute.js File Code is below
import React from "react";
import { Route, Navigate} from 'react-router-dom';
import { isAuthenticated } from "../helper/auth";
//props component is assigned to Component
//...rest spreading props property but reassigning it to a variable called rest
const AdminRoute = ({ component: Component, ...rest }) => {
return (
<Route
{...rest}
render={(props) =>
isAuthenticated() && isAuthenticated().role === 1 ? (
<Component {...props} />
) : (
<Navigate to = '/signin' />
)
}
/>
)
};
export default AdminRoute;
App.js File Code is below
import React from 'react';
import {BrowserRouter, Route, Routes} from 'react-router-dom';
import Header from './Header';
import Home from './Home';
import Signup from './Signup';
import Signin from './Signin';
import ForgotPassword from './forgot-password';
import UserDashboard from './UserDashboard';
import AdminDashboard from './AdminDashboard';
import ShowroomDashboard from './ShowroomDashboard';
import AdminRoute from './AdminRoute';
import NotFound from './NotFound';
const App = () => (<BrowserRouter>
<Header />
<main>
<Routes>
<Route exact path='/' element={<Home />} />
<Route exact path='/signup' element={<Signup />} />
<Route exact path='/signin' element={<Signin />} />
<Route exact path='/forgotpassword' element={<ForgotPassword />} />
<Route exact path='/user/dashboard' element={<UserDashboard />} />
<AdminRoute exact path='/admin/dashboard' element={<AdminDashboard />} />
<Route exact path='/showroom/dashboard' element={<ShowroomDashboard />} />
<Route exact path = '*' element={<NotFound />} />
</Routes>
</main>
</BrowserRouter>
);
export default App;
react-router-dom no longer supports custom route components, preferring now component wrappers that handle the auth logic and render either the children prop or an Outlet for nested routes, or the redirect.
Wrap a single "Route" component:
import React from "react";
import { Navigate } from 'react-router-dom';
import { isAuthenticated } from "../helper/auth";
const AdminRoute = ({ children }) => {
return isAuthenticated()?.role === 1
? children
: <Navigate to='/signin' replace />;
};
...
<Route
path='/admin/dashboard'
element={(
<AuthRoute>
<AdminDashboard />
</AuthRoute>
)}
/>
Wrap nested Route components:
import React from "react";
import { Navigate, Outlet } from 'react-router-dom';
import { isAuthenticated } from "../helper/auth";
const AdminWrapper = () => {
return isAuthenticated()?.role === 1
? <Outlet />
: <Navigate to='/signin' replace />;
};
...
<Route path='/admin/dashboard/*' element={<AdminWrapper />}>
<Route index element={<AdminDashboard />} />
... any other '/admin/dashboard/*' routes ...
</Route>
My react component should work as follows:
Check a global variable for error and if there is error redirect to home and unset the global error variable.
Otherwise it should render a which will use current page's location to redirect to correct location.
How should I go about doing this. The only way which somehow works is if I do conditional render and set global variable in render. Is there any better way?
In this example I'm using context api to share the state across all routes available.
For question No. 1 - use protected route. (Much simpler & neat)
predeclared path & component on <ProtectedRoute path="/anyPath" component={anyComponent}/>
if you want to have flexibility on setting the path yourself, then go to methods No. 2 below
in App.js:-
import React from "react";
import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";
import { ErrorState } from "./contexts/ErrorState";
// import ProtectedRoute from "./comps/routes/ProtectedRoute";
import ProtectedRouteCustom from "./comps/routes/ProtectedRouteCustom";
import Home from "./comps/Home";
import Demo from "./comps/Demo";
import Dashboard from "./comps/Dashboard";
import "./style.css";
export default function App() {
return (
<ErrorState>
<Router>
<NavBar />
<Switch>
<Route exact path="/" component={Home} />
<Demo exact path="/demo" component={Demo} />
{/*} <ProtectedRoute exact path="/demo/dashboard" component={Dashboard} /> */}
<ProtectedRouteCustom path="/demo" />
<Route path="*" component={() => "Not Found"} />
</Switch>
</Router>
</ErrorState>
);
}
ProtectedRoute.js (emphasis more on Route render):-
import React from "react";
import { Redirect, Route } from "react-router-dom";
import { useError } from "../../contexts/ErrorState";
const ProtectedRoute = ({ component: Component, ...rest }) => {
const [errorState, errorDispatch] = useError();
const { error } = errorState;
return (
<Route
{...rest}
render={props => {
// you can use props.location.pathname to redirect user to the route path you have specified in App.js (see in console.log)
console.log(props);
// render Dashboard component if 'error' === false
if (!error) return <Component {...props} />;
// redirect to homepage if error === false
else
return (
<Redirect
to={{
// specify the path to redirect (if condition 'error' === true)
pathname: "/",
state: { from: props.location }
}}
/>
);
}}
/>
);
};
export default ProtectedRoute;
For question No. 2 - you can build your own custom protected route
path can be specified or set it yourself in ProtectedRouteCustom
App.js:-
import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";
import { ErrorState } from "./contexts/ErrorState";
// import ProtectedRoute from "./comps/routes/ProtectedRoute";
import ProtectedRouteCustom from "./comps/routes/ProtectedRouteCustom";
import Home from "./comps/Home";
import Demo from "./comps/Demo";
import Dashboard from "./comps/Dashboard";
import "./style.css";
export default function App() {
return (
<ErrorState>
<Router>
<NavBar />
<Switch>
<Route exact path="/" component={Home} />
<Demo exact path="/demo" component={Demo} />
{/*} <ProtectedRoute exact path="/demo/dashboard" component={Dashboard} /> */}
<ProtectedRouteCustom path="/demo" />
<Route path="*" component={() => "Not Found"} />
</Switch>
</Router>
</ErrorState>
);
}
custom protected route:- (emphasis more on Redirect rather than Route render)
import React from "react";
import { Redirect, Route } from "react-router-dom";
import { useError } from "../../contexts/ErrorState";
import Dashboard from "../Dashboard";
const ProtectedRouteCustom = ({ path }) => {
const [errorState, errorDispatch] = useError();
const { error } = errorState;
return (
<>
{error ? (
<Redirect to="/" />
) : (
<>
<Redirect from={path} to={path + "/dashboard"} />
<Route to={path + "/dashboard"} component={Dashboard} />
</>
)}
</>
);
};
export default ProtectedRouteCustom;
You can see the sandbox here for full working code.
Guidelines on how to use the sandbox code
Environment for No. 1:-
uncomment/enable:
import ProtectedRoute from "./comps/routes/ProtectedRoute";
<ProtectedRoute exact path="/demo/dashboard" component={Dashboard} />
comment/disable:
import ProtectedRouteCondition from "./comps/routes/ProtectedRouteCondition";
<ProtectedRouteCondition path="/demo" />
Environment for No. 2: run as it is
Generally all coder using your methods. If you checking React in Facebook Group. You can find correct answer ı think.
I am trying to use onEnter with my reactjs app. I want to check if the session is true then make sure the user gets redirected to the dashboard page.
I am wondering if anyone knows why this would not be working?
import React, { Fragment } from "react";
import { BrowserRouter as Router, Route } from "react-router-dom";
//import Header from './core/header';
//PAGES
import Homes from './core/pages/home';
import Dashboard from './core/pages/dashboard';
import Addnew from './core/pages/addnew';
import './App.css';
import 'bootstrap/dist/css/bootstrap.min.css';
export function onEnter(nextState, transition, callback) {
console.log("test")
const { pathname } = nextState.location
const isLoggedIn = sessionStorage.getItem('loggedin') === true
if (pathname === '/' && isLoggedIn) {
transition('/dashboard') //redirect to Home component
}
return callback() // go as it is.
}
function App() {
return (
<div className="App">
<Router>
<Fragment>
<Route path="/" exact component={Homes} onEnter={onEnter} />
<Route path="/dashboard" exact component={Dashboard} />
<Route path="/dashboard/radio/:stationid" exact component={Dashboard} />
<Route path="/addnew" exact component={Addnew} />
</Fragment>
</Router>
</div>
);
}
export default App
First, you need to also import Redirect from react-router-dom
import { BrowserRouter as Router, Route, Redirect } from "react-router-dom";
Not exactly sure about your logic but you need to call Redirect like the following;
<Redirect
to={{
pathname: "/dashboard",
state: { from: location }
}}
/>
refer to the react-router doc https://reacttraining.com/react-router/web/example/auth-workflow
From react-router-v4 onEnter, onUpdate, and onLeave is removed,
Docs :https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/guides/migrating.md#on-properties
Try something like
<Route exact path="/home" render={() => (
isLoggedIn() ? (
<Redirect to="/first"/>
) : (
<Home />
)
)}/>