Search code examples
javascriptreactjsreact-router-dom

React, implementing protected route with authentication with React Router Dom


Unable to route to the 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;

Solution

  • 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();
    
      // 👇🏽 could be a check inside a global state, like a context
      if(!localStorage.getItem("loginEmail")){
        return <Navigate  to="/login" state={{ from: location }}  replace />;
      }
    
      return children;
    };
    

    That location passed as props to Navigate can be used inside Login to send the user to that specific url where they were going instead of a hard-coded one after they have logged in, useful if you have multiple protected routes. It's not a requirement though, 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.