This question already has answers here:
How to create a protected route with react-router-dom?
(5 answers)
Closed 9 months ago.
Unable to route to admin page even after successful login, displays a blank screen. On successful login system should navigate and display the admin screen. Could someone please advise why the routing is not happening ?
//login.js
import React, { useEffect, useState } from 'react';
import { useForm } from "react-hook-form";
import { useNavigate } from "react-router-dom";
const Login = () =>{
const { register, errors, handleSubmit } = useForm();
const [loginData, setLoginData] = useState("");
const [helperText, setHelperText] = useState('');
const navigate = useNavigate();
const onSubmit = (data) => {
try {
const userEmail = "dev#test.com"; // for time being just hard code data
const userPassword = "somePass123"; // for time being just hard code data
if(data.email === userEmail && data.password === userPassword ){
localStorage.setItem('loginEmail', userEmail);
setLoginData(userEmail);
navigate('/admin');
window.location.reload(true)
} else {
setHelperText("Invalid login details");
}
} catch (e){
console.log(e);
}
};
console.log(errors);
return (
<div className="wrapper">
<h3>Login</h3>
<section className="col2">
<div className='loginSection'>
<form onSubmit={handleSubmit(onSubmit)}>
<label>Email</label>
<input
type="text"
{...register("email", { required: true})}
/>
<label>Password</label>
<input
type="text"
{...register("password", { required: true})}
/>
<label>
<span className="loginValidationText">{helperText}</span>
</label>
<section className="col4">
<input type="submit" />
</section>
</form>
</div>
</section>
</div>
)
}
export default Login
//protectedRoute.js
import React from "react";
import { Route, BrowserRouter } from "react-router-dom";
export const ProtectedRoute = ({ component: Component, ...rest }) => {
return (
<Route
{...rest}
render={(props) => {
if (localStorage.getItem("loginEmail")) {
return <Component {...props} />;
} else {
return (
<>
<BrowserRouter
to={{
pathname: "/login",
state: {
from: props.location,
},
}}
/>
</>
);
}
}}
/>
);
};
//navigation.js
import React from 'react';
import { NavLink} from 'react-router-dom';
const Navigation = () => {
return (
<div className="App">
<div className="wrapper">
<div id="wrap">
<nav className="siteNavigation_nav_links">
<div className="main_links_nav">
<img className='logoimage' alt="SSS Logo" src="/images/super.png"></img>
<div className="navigationpanel">
<NavLink className="mob_link" to="/">Home</NavLink>
<NavLink className="mob_link" to="/team">Team</NavLink>
<NavLink className="mob_link" to="/login">Login</NavLink>
</div>
</div>
</nav>
</div>
</div>
</div>
)
}
export default Navigation;
//App.js
import React, { useEffect, useState } from 'react';
import { BrowserRouter, Route, Routes, Switch} from "react-router-dom";
import Navigation from './components/navigation';
import Home from "./components/home";
import Team from "./components/team";
import Admin from "./components/admin";
import Login from "./components/login";
import { ProtectedRoute } from "./components/protectedRoute";
function App() {
return (
<BrowserRouter>
<Navigation />
<Routes>
<Route path="/" element={<Home />}>
</Route>
<Route path="/team" element={<Team />}>
</Route>
<Route path="/login" element={<Login />}>
</Route>
<Route path="/admin" element={
<ProtectedRoute >
<Admin />
</ProtectedRoute>
}>
</Route>
</Routes>
</BrowserRouter>
);
}
export default App;
Change PotectedRoute.js to the code below, as what you are doing is more like what we used to do for React Router Dom v5 while you are using v6.
import { Navigate, useLocation} from "react-router-dom";
export const ProtectedRoute = ({children}) => {
let location = useLocation();
if(!localStorage.getItem("loginEmail")){
return <Navigate to="/login" state={{ from: location }} replace />;
}
return children;
};
The information from useLocation passed as prop to Navigate can be used in Login so you send the user to that specific url where they were going to instead of a hard coded one (admin in your case), useful if you had multiple protected routes. Though it's not a requirement, you can remove it.
To know more about authentication in React Router Dom v6, visit this example on StackBlitz from their documentation. Don't look at the editor lint errors, it's the most complete and straightforward authentication example.
Related
I have two(USER and ADMIN) users in my react project and I want to redirect them to their respected page after a successful login. To achieve this I created a standalone component called authenticatedRoute.js and wrap those components by AuthenticatedRoute component. Here are the codes
AuthenticatedRoute.js
import { useHistory } from "react-router-dom";
import { Route } from "react-router-dom";
const AuthenticatedRoute = ({ children, ...rest }) => {
const history = useHistory();
var tokenn = JSON.parse(localStorage.getItem("desta"));
var user_type = tokenn?.user.roles[0]; //USER or ADMIN
var loggedIn = tokenn?.user_status; //true
const redirect = () => {
try {
switch (user_type) {
case "USER":
return [history.push("/user"), children];
case "ADMIN":
return [history.push("/admin"), children];
default:
console.log("redirect");
}
} catch (err) {
return [history.push("/signin"), children];
}
};
return (
<Route
render={() => {
return loggedIn ? redirect() : history.push("/signin");
}}
/>
);
};
export default AuthenticatedRoute;
App.js (minimal code)
import "./App.css";
import Header from "./components/Header/Header";
import HeaderLinks from "./components/Header/HeaderLinks";
import Hero from "./components/Header/Hero/Hero";
import Login from "./components/auth/Login";
import {
BrowserRouter as Router,
Route,
Switch,
useHistory,
} from "react-router-dom";
import SignUp from "./components/auth/SignUp";
import ViewUserProfile from "./components/Admin/ViewUserProfile";
import AdminHeader from "./components/Admin/AdminHeader";
import ManageBusinessType from "./components/Admin/ManageBusinessType";
import ManageCompany from "./components/Admin/ManageCompany";
import UserDashboard from "./components/Users/UserDashboard";
import ViewProfile from "./components/Users/ViewProfile";
import { ResetPassword } from "./components/Users/ResetPassword";
import ChangePassword from "./components/Users/ChangePassword";
import { useEffect } from "react";
import AuthenticatedRoute from "./components/auth/authRoute/AuthenticatedRoute";
function App() {
return (
<Router>
<Switch>
<AuthenticatedRoute path="/" exact>
<Hero />
</AuthenticatedRoute>
<AuthenticatedRoute path="/admin" exact>
<AdminHeader />
</AuthenticatedRoute>
<AuthenticatedRoute path="/user" exact>
<UserDashboard />
</AuthenticatedRoute>
</Switch>
</Router>
);
}
export default App;
After a successful login it redirects me to the right user role page but it's empty page as the screenshot below.
How can I fix the Issue? I used react-router-dom#5.3.0
Thanks
You are rendering the result of a navigation action instead of JSX. AuthenticatedRoute should render either a Route rendering the routed content or a Redirect to the appropriate path.
Example:
If loggedIn is falsey, then redirect to "/signin", otherwise check the role the route should have access to, and if the role matches render a Route with the props passed through, otherwise redirect to the appropriate user/admin path.
import { useHistory } from "react-router-dom";
import { Route } from "react-router-dom";
const AuthenticatedRoute = ({ roles, ...props }) => {
const tokenn = JSON.parse(localStorage.getItem("desta"));
const user_type = tokenn?.user.roles[0]; //USER or ADMIN
const loggedIn = tokenn?.user_status; //true
return loggedIn
? roles.includes(user_type)
? <Route {...props} />
: <Redirect to={user_type === "USER" ? "/user" : "/admin"} />
: <Redirect to="/signin" />;
};
export default AuthenticatedRoute;
Specify the roles a route should accessible by.
function App() {
return (
<Router>
<Switch>
<AuthenticatedRoute path="/admin" roles={["ADMIN"]}>
<AdminHeader />
</AuthenticatedRoute>
<AuthenticatedRoute path="/user" roles={["USER"]}>
<UserDashboard />
</AuthenticatedRoute>
<Route path="/">
<Hero />
</Route>
</Switch>
</Router>
);
}
I'm working on react Disney+ clone etc and I was trying to do something like: if user isnt authorized then show login page but if authorized then show content. I used useHistory for this. And it works for a second, it just starts to download login page (background image is loading, but text of login page is visible) and then it disappears and content page is shown. Url changes for a second too.
App.js
function App() {
return (
<div className="App">
<Router>
<Header />
<Switch>
<Route path="/login">
<Login/>
</Route>
<Route path="/detail/:id">
<Detail/>
</Route>
<Route path="/">
<Home/>
</Route>
</Switch>
</Router>
</div>
);
}
Header.js
import React from 'react'
import {selectUserName, selectUserPhoto, setUserLogin, setUserSignOut} from '../../features/user/userSlice' ;
import { useDispatch, useSelector } from "react-redux" ;
import { auth, provider} from "../../firebase"
import { useHistory} from 'react-router-dom';
const Header = () => {
const dispatch = useDispatch()
const userName = useSelector(selectUserName);
const userPhoto = useSelector(selectUserPhoto);
const history = useHistory();
const signIn = () => {
auth.signInWithPopup(provider)
.then((result) => {
let user = result.user;
dispatch(setUserLogin({
name: user.displayName,
email: user.email,
photo: user.photoURL
}))
history.push('/');
})
}
const signOut = () => {
auth.signOut()
.then(() => {
dispatch(setUserSignOut());
history.push('/login');
})
}
}
Based on your issue you can handle Routes in a different way. So you have routes which can only shown during unauthorised situation and some routes only shown for authorised user. For that you can have following implementation.
First you can create ProtectedRoute function.
import React from "react";
import { Redirect, Route } from "react-router-dom";
function ProtectedRoute({ component: Component, ...restOfProps }) {
const isAuthenticated = localStorage.getItem("isAuthenticated");
console.log("this", isAuthenticated);
return (
<Route
{...restOfProps}
render={(props) =>
isAuthenticated ? <Component {...props} /> : <Redirect to="/login" />
}
/>
);
}
export default ProtectedRoute;
And then you can use this function in your main App where you will declare your routes with component.
import ProtectedRoute from "./component/ProtectedRoute";
function App() {
return (
<div className="App">
<BrowserRouter>
<Route path="/login" component={Login} />
<ProtectedRoute path="/protected" component={ProtectedComponent} />
</BrowserRouter>
</div>
);
}
export default App;
I am newbie in react-router-dom and facing this issue for first time!
Whenever I Click on Logout Button on Homs.js File it gives me following error: TypeError: setauth is not a function
auth will be set true from login as well as signup function
Tysm for helping in advance!
App.js
import React,{useState} from 'react';
//import Navbar from './components/Navbar';
import {BrowserRouter as Router, Route ,Switch, Redirect} from 'react-router-dom';
import Home from './components/Home';
import Login from './components/Login';
import SignUp from './components/SignUp';
import ViewAllCustomers from './components/ListCustomer';
import AllTransactions from './components/Transactions';
const App = (props) => {
const [auth,setAuth] = useState(false);
const setauth = boolean => {
setAuth(boolean);
};
return (<div >
<Router>
<Switch>
<Route exact path="/"
render ={ props => !auth?(<Home {...props} seauth={setauth}/>):(<Redirect to="/login" />) }
/>
<Route path="/login"
render = {props => !auth?(<Login {...props} seAuth={setauth} />):(<Redirect to="/" />)} />
<Route path="/signup"
render = {props => !auth?(<SignUp {...props} setAuth={setauth}/>):(<Redirect to="/" />)}
/>
<Route path="/customer">
<ViewAllCustomers />
</Route>
<Route path="/transfers">
<AllTransactions />
</Route>
</Switch>
</Router>
</div>);
}
export default App;
Home.js
import React from'react';
const Home = ({ setauth }) => {
const logout = (e) => {
e.preventDefault();
setauth(false);
}
return (<div className="container">
<h1>
Home Page
</h1>
<button className="btn btn-danger mt-5" onClick={logout}>Logout</button>
</div>);
}
export default Home;
Tysm for helping in advance!
auth will be set true from login as well as signup function
You pass seauth={setauth}, but in Home.js you still use setauth. Either fix grammar mistakes or use seauth in Home.js
Edit: Adding to my answer, you should really use camelCase or snake_case throughout your project, as it improves readability.
I'm trying to create triple nesting with react-router but it's not working.
Here is the expected output
I want to perform but I am trying to implement this with the router so my tab should get switch according to URL passed to it.
When the user just write / in the URL it should redirect to login page (working)
When the user write /dashboard it should redirect to the dashboard with side list (working)
When the user click on list items such as about in drawer of the dashboard then it should render about page.
Now my about page contains two tabs one for general and one for account (Everything was working until I implement this feature now the whole app has been crashed and not even showing the error)
Here is my code in sandbox. Note: I have commented on my third nested route because it was crashing entire app
App.js
import { Switch, Route } from "react-router-dom";
import Login from "./pages/Login";
import Dashboard from "./pages/Dashboard";
import "./styles.css";
export default function App() {
return (
<div className="App">
<Switch>
<Route path="/dashboard" component={Dashboard} />
<Route path="/" exact component={Login} />
</Switch>
</div>
);
}
Dashboard.js for nested routing
import { Switch, Redirect, Route, useRouteMatch } from "react-router-dom";
import Home from "./Home";
import About from "./About";
import AppDrawerBar from "../components/AppDrawerBar";
const Dashboard = () => {
const { path } = useRouteMatch();
return (
<>
<AppDrawerBar>
<Switch>
<Route path={`${path}/about`} component={About} />
<Route path={`${path}/home`} component={Home} />
<Redirect to={`${path}/home`} />
</Switch>
</AppDrawerBar>
</>
);
};
export default Dashboard;
About.js third nested routing to handle tab navigation after clicking on about list item
import { Switch } from "#material-ui/core";
import React from "react";
import { Redirect, Route } from "react-router-dom";
import TabMenu from "../components/TabMenu";
const About = (props) => {
return (
<>
<h1>Welcome to about</h1>
{/* This doesn't work.. this code is to render tab navigation */}
{/* <Switch>
<Redirect exact from={`${props.path}/about`} to="about/general" />
<Route
exact
path="/about/:page?"
render={(props) => <TabMenu {...props} />}
/>
</Switch> */}
</>
);
};
export default About;
TabMenu.js to handle my tabs
import { Paper, Tab, Tabs } from "#material-ui/core";
import React, { useState } from "react";
import General from "../pages/General";
import Account from "../pages/Account";
const TabMenu = (props) => {
const { match, history } = props;
const { params } = match;
const { page } = params;
const tabNameToIndex = {
0: "about",
1: "contact"
};
const indexToTabName = {
about: 0,
contact: 1
};
const [selectedTab, setSelectedTab] = useState(indexToTabName[page]);
const handleChange = (event, newValue) => {
history.push(`/about/${tabNameToIndex[newValue]}`);
setSelectedTab(newValue);
};
return (
<>
<Paper elevation={2}>
<Tabs
value={selectedTab}
onChange={handleChange}
indicatorColor="primary"
textColor="primary"
>
<Tab label="General" />
<Tab label="Profile" />
</Tabs>
</Paper>
{selectedTab === 0 && <General />}
{selectedTab === 1 && <Account />}
</>
);
};
export default TabMenu;
I was trying to restrict logged in user to access login page using following code
import React, { useEffect, useState } from "react"; import { Route }
from "react-router-dom"; import { Redirect } from "react-router-dom";
const UserLayoutRoute = ({ component: Component, ...rest }) => {
const [loggedIn, setLoggedIn] = useState(null); useEffect(() => {
if (localStorage.getItem("cachedValue") !== null) {
setLoggedIn(true);
} }, []); return loggedIn ? (
<Route
{...rest}
render={matchProps => (
<div className="App">
<section className="user-page">
<div className="">
<div className="">
<Component {...matchProps} />
</div>
</div>
</section>
</div>
)}
/> ) : (
<Redirect to="/" /> ); };
export default UserLayoutRoute;
With this code page keep on loading and its not rendering anything.
I also posted this issue in GitHub https://github.com/facebook/react/issues/17514
I think that maybe you can try other approach like this
import React from 'react';
import PropTypes from 'prop-types';
import { Route, Redirect } from 'react-router-dom';
const PrivateRouteComponent = ({ component: Component, isAuth, ...rest }) => (
<Route
{...rest}
render={props => (
isAuth
? <Component {...props} />
: <Redirect to="/login" />
)}
/>
);
PrivateRouteComponent.propTypes = {
component: PropTypes.any.isRequired,
isAuth: PropTypes.bool.isRequired,
};
export default PrivateRouteComponent;
And in the case the routes
<Switch>
<PrivateRouteComponent exact path="/" component={**ComponentName**} isAuth={isAuth} />
</Switch>
For the case that the isAuth props maybe you can change that for your condition