Protected routes not accessible even after signing or logging in

I am making a site whereby after the user signs in, the user is meant to be redirected to the home page. The homepage and all the other pages of the site are only accessible by signed in users but even after a user signs in(firebase auth), the rest of the site(protected routes) is still not accessible and the only page accessible is the login page. The technologies I am using are react, react router dom and firebase and this is how my code looks like, starting with the App.js

import Home from "./pages/home/Home";
import Login from "./pages/login/Login";
import { BrowserRouter, Navigate, Route, Routes } from "react-router-dom";
import List from "./pages/list/List";
import User from "./pages/user/User";
import AddNew from "./pages/addnew/AddNew";
import { useContext } from "react";
import { AuthContext } from "./context/AuthContext";

function App() {
  const {currentUser} = useContext(AuthContext);
  
  const RequireAuth = ({ children }) => {
    return currentUser ? children : <Navigate to="/login" />;
  };


  return (
    <div className="App">
      <BrowserRouter>
        <Routes>
          <Route path="/login" exact element={<Login />} />
          <Route path="/" exact element={ <RequireAuth> <Home /> </RequireAuth> } />
          <Route path="/users" exact element={<RequireAuth><List /></RequireAuth>} />
          <Route path="/users/:id" exact element={<RequireAuth><User /></RequireAuth>} />
          <Route path="/add" exact element={<RequireAuth><AddNew /></RequireAuth>} />
        </Routes>
      </BrowserRouter>
    </div>
  );
}

export default App;

And then followed by the login.js page

import React,{useState} from 'react'
import { useContext } from 'react';
import { signInWithEmailAndPassword } from "firebase/auth";
import { auth } from '../../firebase';
import "./login.css";
import {useNavigate} from "react-router-dom";
import { AuthContext } from '../../context/AuthContext';

export default function Login() {
  const [error, seterror] = useState(false);
  const [email, setemail] = useState("");
  const [password, setpassword] = useState("");
  const navigate = useNavigate();
  const {dispatch} = useContext(AuthContext)
  
  const handleLogin = (e) => {
    e.preventDefault();

    signInWithEmailAndPassword(auth, email, password)
      .then((userCredential) => {
        const user = userCredential.user;
        dispatch({type: "LOGIN", payload: user});
        navigate("/");
      })
      .catch((error) => {
        seterror(true);
        console.log(error.message);
      });
  }


  return (
    <div className='login'>
        <form onSubmit={handleLogin}>
          <input className='ok' type="email" placeholder='email' onChange={e => setemail(e.target.value)} />
          <input className='ok' type="password" placeholder='password' onChange={e => setpassword(e.target.value)} />
          <button className='sb'>Submit</button>
          {error && <span className='ks'>Wrong email or password</span>}
        </form>
    </div>
  )
}

And then I have the authreducer.js file that deals with the state

const AuthReducer = (state, action) => {
    switch (action.type) {
        case "LOGIN": {
            return {
                currentUser: action.payload,
            }
        }
        case "LOGOUT": {
            return {
                currentUser: null
            }
        }
        default:
            return state;
    }
}

export default AuthReducer

And finally the authcontext.js file

import { createContext, useEffect, useReducer } from "react";
import AuthReducer from "./AuthReducer";

const INITIAL_STATE = {
    currentUser: JSON.parse(localStorage.getItem("user")) || null,
}

export const AuthContext = createContext(INITIAL_STATE);

export const AuthContextProvider = ({children}) => {
    const [state, dispatch] = useReducer(AuthReducer, INITIAL_STATE);

    useEffect(() => {
        localStorage.setItem("user", JSON.stringify(state.currentUser))
    }, [state.currentUser])

    return (
        <AuthContext.Provider value={{current: state.current, dispatch}}>
            {children}
        </AuthContext.Provider>
    )
}

I do not know what could be causing this problem but I have an idea that it has something to do with the state because it was redirecting well before I started combining it with the state. What could be the problem

>Solution :

Issue

From that I can see, the App isn’t destructuring the correct context value to handle the conditional route protection.

The AuthContextProvider provides a context value with current and dispatch properties

<AuthContext.Provider value={{ current: state.current, dispatch }}>
  {children}
</AuthContext.Provider>

but App is accessing a currentUser property, which is going to be undefined because state.current is undefined.

const { currentUser } = useContext(AuthContext);

const RequireAuth = ({ children }) => {
  return currentUser ? children : <Navigate to="/login" />;
};

The Navigate component will always be rendered.

Solution

Assuming the handleLogin handler correctly updates the state then the solution is to be consistent with state properties.

<AuthContext.Provider value={{ currentUser: state.currentUser, dispatch }}>
  {children}
</AuthContext.Provider>

const { currentUser } = useContext(AuthContext);

const RequireAuth = ({ children }) => {
  return currentUser ? children : <Navigate to="/login" />;
};

Leave a Reply