React Router Dom 6 page redirected to sign in page even when user is signed in

I’m using firebase for user authentication and react-router-dom 6 for private routes. The "/account" page is protected and wrapped inside private routes. I have a Nav component, which has an icon that redirects to the "/account" page, the code is as follows:

export default function Nav() {
    const navigate = useNavigate();

    return (
        <>
         <nav className='navbar'>
            <div className="account-container">
                <RiUser3Fill className='account-icon menu-icon' onClick={()=>{navigate("account")}}/>
                <BsCartFill className='cart-icon menu-icon' onClick={()=>{navigate('cart')}}/>
            </div>
        </nav>
        <Outlet/>    
        </>
    )
}

When I click on the account icon and the user is logged in, the page would redirect to the protected account page. But the problem is, when I refresh the page at "/account", or type in the URL to get to "/account", the page would always be redirected to "/signin" page even when the user is already signed in. Below are my other components:

App.js:

function App() {
  return <BrowserRouter>
  <AuthProvider>
    <Routes>
      <Route path="/" element={<Nav/>}>
        <Route element={<PrivateRouter/>}>
          <Route path="/account" element={<Account/>}/>
          <Route path="/cart" element={<Cart/>}/>
        </Route>
        <Route path='/signup' element={<Signup/>}/>
        <Route path='/signin' element={<Signin/>}/>
        </Route>
    </Routes>
  </AuthProvider>
  </BrowserRouter>
}

PrivateRouter.jsx:

export default function PrivateRouter() {
    const {currentUser} = useAuth();
    const location = useLocation();
    if(!currentUser) return <Navigate state={{from:location}} to="/signin"/>
    return <Outlet />
}

AuthContext.js:

import React, {useContext, useEffect, useState} from 'react';
import { signInWithEmailAndPassword, createUserWithEmailAndPassword, signOut, onAuthStateChanged,
passwo } from 'firebase/auth';
import { auth } from '../utility/firebase';

const AuthContext = React.createContext(); 

export function useAuth(){
    return useContext(AuthContext);
}

export default function AuthProvider({children}) {
    const [currentUser, setCurrentUser] = useState();

    useEffect(()=>{
        const unsub = onAuthStateChanged(auth,user=>{
            setCurrentUser(user);
        })
        return unsub;
    },[])

    function signin(email,password){
        return signInWithEmailAndPassword(auth,email,password);
    }

    function signup(email,password){
        return createUserWithEmailAndPassword(auth,email,password);
    }

    function signout(){
        return signOut(auth);
    }


const values = {
    currentUser,
    signin,
    signup,
    signout
}

  return <AuthContext.Provider value={values}>
      {children}
  </AuthContext.Provider>;
}

Here is Solutions:

We have many solutions to this problem, But we recommend you to use the first solution because it is tested & true solution that will 100% work for you.

Solution 1

The currentUser initial value is undefined until updated by the auth check in the useEffect.

const [currentUser, setCurrentUser] = useState(); // <-- undefined

useEffect(() => {
  const unsub = onAuthStateChanged(auth, user => {
    setCurrentUser(user); // <-- sets user state after initial render
  });

  return unsub;
}, []);

So when refreshing the page, i.e. remounting the app, the currentUser condition in the auth check is falsey and user is bounced to login/signin page.

If the currentUser is still undefined, i.e. the app hasn’t determined/confirmed either way a user’s authentication status, you should return null and not commit to redirecting or allowing access through to the routed component.

export default function PrivateRouter() {
  const { currentUser } = useAuth();
  const location = useLocation();

  if (currentUser === undefined) return null; // or loading spinner, etc...

  return currentUser
    ? <Outlet />
    : <Navigate to="/signin" replace state={{ from: location }} />;
}

Note: Use and implement solution 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply