'React Routing unable to route properly

I am trying to create a route for admin pages. Codes can be found below. My current issue is that my currentUser has my authenticated user obj and userType is False, but I still can access /createUser page.

AdminRoute.js

import React from 'react' 
import { Route, Redirect } from 'react-router-dom' 
import { useAuth } from '../../contexts/AuthContext' 
 
const AdminRoute = (props) => { 
    const { currentUser } = useAuth(); 
    const userType = localStorage.getItem('admin'); 
 
    if (currentUser === undefined) { 
        return null; 
    } 
    console.log(userType) 
 
    return currentUser && userType 
    ? ( 
      <Route {...props} /> 
    ) 
    : ( 
      <Redirect to={{ 
        pathname: "/homepage", 
        state: { 
          from: props.location 
        } 
      }} /> 
    ) 
} 
 
export default AdminRoute

Route.js

<Router> 
     <Switch>                
          <AdminRoute path='/createUser' component={Register} /> 
     </Switch>
</Router>

Step 1: Login.js

async function handleSubmit(e) { 
        e.preventDefault() 
        try { 
            setError('') 
            setLoading(true) 
            await login(emailRef.current.value, passwordRef.current.value) 
 
            history.push('/homepage') 
        } catch { 
            console.log("heh") 
        } 
 
        setLoading(false) 
    }

Step 2: HandleSubmit will call function from AuthContext.js.

This is the AuthContext.js where I get the currentUser after authenticating at firebase

import React, { useContext, useState, useEffect } from "react" 
import { auth, database } from "../firebase"; 
import { getDocs, query, where } from "firebase/firestore"; 
 
const AuthContext = React.createContext() 
 
export function useAuth() { 
    return useContext(AuthContext) 
} 
 
export function AuthProvider({ children }) { 
    const [currentUser, setCurrentUser] = useState(null) 
    const [loading, setLoading] = useState(true) 
 
    function login(email, password) { 
        return auth.signInWithEmailAndPassword(email, password).then(() => { 
            const Doc = query(database.usersRef, where("email", "==", email)); 
 
            getDocs(Doc).then((querySnapshot) => { 
                let values = ''; 
 
                querySnapshot.forEach((doc) => { 
                    values = doc.id; 
                    
                    // Setting user type in session storage 
                    if (doc.data().userType === "Administrator") { 
                        localStorage.setItem('admin', false); 
                    } else { 
                        localStorage.setItem('admin', false); 
                    }
                }); 
 
                var userUpdate = database.usersRef.doc(values); 
                userUpdate.update({ 
                    lastActive: new Date().toLocaleString('en-SG'), 
                }) 
            }) 
        }); 
    } 
 
    function logout() { 
        return auth.signOut(); 
    } 
 
    function forgetPassword(email) { 
        return auth.sendPasswordResetEmail(email); 
    } 
 
    function updateEmail(email) { 
        return currentUser.updateEmail(email) 
    } 
 
    function updatePassword(password) { 
        return currentUser.updatePassword(password) 
    } 
 
    function updateDisplayName(name) { 
        return currentUser.updateDisplayName(name) 
    } 
 
    useEffect(() => { 
        const unsubscribe = auth.onAuthStateChanged( user => { 
            setLoading(false) 
            setCurrentUser(user) 
        }) 
 
        return unsubscribe 
    }, []) 
 
    const value = { 
        currentUser, 
        login, 
        forgetPassword, 
        logout, 
        updateEmail, 
        updatePassword, 
        updateDisplayName, 
    } 
 
    return ( 
        <AuthContext.Provider value={value}> 
            {!loading && children} 
        </AuthContext.Provider> 
    ) 
}


Solution 1:[1]

I think I see the issue. The code is not JSON serializing/deserializing the "admin" state it is persisting and loading from localStorage.

In your console run:

localStorage.setItem('admin', false)
const userType = localStorage.getItem('admin')
typeof userType
userType

enter image description here

localStorage.getItem('admin'); returns a JSON string, "false", and when you console log it you don't see the quotes. "false" is a truthy value, so the currentUser && userType boolean expression evaluates true when a user is authenticated and there was a value stored in localStorage under the "admin" key.

You should always JSON serialize/deserialize to/from localStorage.

const AdminRoute = (props) => { 
  const { currentUser } = useAuth(); 
  const userType = JSON.parse(localStorage.getItem('admin') ?? false); 
 
  if (currentUser === undefined) { 
    return null; 
  } 
 
  return currentUser && userType 
    ? ( 
      <Route {...props} /> 
    ) 
    : ( 
      <Redirect to={{ 
        pathname: "/homepage", 
        state: { 
          from: props.location 
        } 
      }} /> 
    );
}

login

...

// Setting user type in session storage 
localStorage.setItem('admin', JSON.stringify(doc.data().userType === "Administrator"));

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Drew Reese