'State will not stay updated to keep a user logged in, even though the token remains in localStorage

I'm creating a basic app using React and Express. The issue I'm having is that I can login a user and register a new user, but when I hit the refresh button, it logs the same user out, immediately. In addition, their authorization token is still present in the localStorage even though the user is logged out. I don't think its coming from the server because the server (via Postman) performs all necessary functions as needed. The error has to be coming from the front end. In addition, I had my actions in state set within a try/catch block. I removed the catch for errors block because whenever I would register/login a user, it would perform the behaviors as needed, but the authorization would come back as a 401. So in short, I could register a user, but I couldn't login a user, nor keep that new user logged in. Once I removed the try/catch block and just created a basic async/await function, it would allow me to register/login a user without any issues....except now, I can't stay logged in after refreshing the page. Here is my code.

Register.js

...imports

const Register = () => {
    const alertContext = useContext(AlertContext)
    const authContext = useContext(AuthContext)
    const { setAlert } = alertContext
    const { register, error, clearErrors, isAuthenticated, loggedIn, loadUser } = authContext
    const [user, setUser] = useState({
        name: '',
        email: '',
        password: '',
        password2: '',
    })
    const [ activeUser, setActiveUser ] = useState()
    const { name, email, password, password2 } = user
    

    useEffect(() => {
        const loggedInUser = localStorage.getItem('token')
        if(loggedIn === true && loggedInUser){
            loadUser()
            setActiveUser(loggedInUser)
        }

      if(error === 'User already exists'){
            setAlert(error, 'danger')
            clearErrors()
        }
        // eslint-disable-next-line
    }, [error])

    const onChange = (e) => {
        setUser({ ...user, [e.target.name]: e.target.value })
    }

    const onSubmit = (e) => {
        e.preventDefault()
        if (name === '' || email === '' || password === '') {
            setAlert('Please enter all fields', 'danger')
        } else if (password !== password2) {
            setAlert('Passwords do not match', 'danger')
        } else {
            register({
                name,
                email,
                password
            })
        }
    
    }
    
    if (isAuthenticated) return <Navigate to='/' />
    
    return (
        <div className='form-container'>
            <h1>
                Account <span className='text-primary'>Register</span>
            </h1>
            <form onSubmit={onSubmit}>
                <div className='form-group'>
                    <label htmlFor='name'>Name</label>
                    <input type='text' name='name' value={name} onChange={onChange} />
                    <label htmlFor='email'>Email</label>
                    <input type='email' name='email' value={email} onChange={onChange} />
                    <label htmlFor='password'>Password</label>
                    <input
                        type='password'
                        name='password'
                        value={password}
                        onChange={onChange}
                        minLength='6'
                    />
                    <label htmlFor='password2'>Confirm Password</label>
                    <input
                        type='password'
                        name='password2'
                        value={password2}
                        onChange={onChange}
                        minLength='6'
                    />
                </div>
                <button
                    type='submit'
                    className='btn btn-primary btn-block'
                > Register
                </button>
            </form>
        </div>
    )
}

export default Register

AuthState.js

imports..
const AuthState = ({ children }) => {
    const intitialState = {
        token: localStorage.getItem("token"),
        isAuthenticated: null,
        loading: true,
        error: null,
        user: null,
        loggedIn: false
    }

    const [state, dispatch] = useReducer(AuthReducer, intitialState)

    //Load User
    const loadUser = async () => {
        const { token } = state
        
        const config = {
            headers: {
                'Content-type': 'application/json',
                'x-auth-token': token
            }
        }
        
        const res = await axios.get('/api/auth', config)
            dispatch({
                type: USER_LOADED,
                payload: res.data
            })
    }
    
    //Login User
    const login = async (formData) => {
        const config = {
            headers: {
                'Content-type': 'application/json'
            }
        }

        const res = await axios.post('/api/auth', formData, config)
            dispatch({ 
                type: LOGIN_SUCCESS, 
                payload: res.data
            })
        
            loadUser()  
    }



    //Register User
    const register = async (formData) => {
        const config = {
            headers: {
                'Content-type': 'application/json',
            }
        }

        const res = await axios.post('/api/users', formData, config)
         dispatch({ 
            type: REGISTER_SUCCESS, 
            payload: res.data
         })     
         
    }

    
    //Logout
    const logout = () => {
        localStorage.removeItem('token')
        dispatch({
            type: LOGOUT
        })
    }

    //Clear Errors
    const clearErrors = () => {
        dispatch({ type: CLEAR_ERRORS})
    }

    const { token, isAuthenticated, loading, error, user, loggedIn } = state

    return (
        <authContext.Provider
            value={{
                token,
                isAuthenticated,
                loading,
                error,
                user,
                loggedIn,
                register,
                clearErrors,
                loadUser,
                login,
                logout
            }}
        >
            {children}
        </authContext.Provider>
    )
}

export default AuthState

PrivateRoute.js

const PrivateRoute = ({ component: Component }) => {
  const authContext = useContext(AuthContext)
    const { isAuthenticated, loggedIn } = authContext
   
    if (isAuthenticated || loggedIn) return <Component />;
    return <Navigate to='/login' />;
}

Navbar.js (for logging out)

const Navbar = ({ title, icon }) => {
    const authContext = useContext(AuthContext)
    const { isAuthenticated, logout, user } = authContext

    const onLogout = () => {
        logout()
    }

    const authLinks = (
        <>
         <li> Hello { user && user.user.name }</li>
         <li> 
             <a onClick={onLogout} href="#!">
                 <i className="fas fa-sign-out-alt" /><span className="hide-sm">Logout</span>
             </a>
         </li>
        </>
    )
  
    const guestLinks = (
        <>
         <li>
             <Link to='/register'>Register</Link>
         </li>
         <li>
             <Link to='/login'>Login</Link>
          </li>
        </>
    )
  
    return (
      <div className="navbar bg-primary">
          <h1>
              <i className={icon} /> {title}
          </h1>
          <ul>
             { isAuthenticated ? authLinks : guestLinks }
          </ul>
      </div>
  ) 
}

AuthReducer.js

imports..
export default (state, action) => {
    switch(action.type) {
        case REGISTER_SUCCESS:
            localStorage.setItem('token', action.payload.token)
            return {
                ...state,
                ...action.payload,
                isAuthenticated: true,
                loading: false,
                loggedIn: true
            }
        //case REGISTER_FAIL:
        //case AUTH_ERROR:
        //case LOGIN_FAIL:
        case LOGOUT:
             return {
                ...state,
                token: null,
                isAuthenticated: false,
                loading: false,
                user: null,
                error: action.payload,
                loggedIn: false
            }
        case CLEAR_ERRORS:
            return {
                ...state,
                error: null
            }
        case USER_LOADED:
            return {
                ...state,
                isAuthenticated: true,
                loading: false,
                user: action.payload,
                loggedIn: true
            }
        case LOGIN_SUCCESS:
         // eslint-disable-next-line
        case REGISTER_SUCCESS:
          localStorage.setItem('token', action.payload.token)
            return {
                ...state,
                ...action.payload,
                isAuthenticated: true,
                loading: false,
                loggedIn: true
            }        
        default:
            throw Error(`Unhandled type: ${action.type}, ${action.payload}`)
    }
}

App.js

...
import AuthState from './components/context/auth/AuthState'
import Register from './components/auth/Register'
import Login from './components/auth/Login'
import PrivateRoute from './components/routing/PrivateRoute'


const App = () => {
    return (
        <AuthState>
            <ContactState>
        <AlertState>
        <Router>
            <div className='App'>
                <Navbar />
                <div className='container'>
                  <Alerts />
                <Routes>
                <    Route path='/' element={<PrivateRoute component={Home} />} />
                    <Route path='/about' element={<About />} />
                    <Route path='/register' element={<Register />} />
                    <Route path='/login' element={<Login />} />
                </Routes>
                    </div>
                </div>
                </Router>
        </AlertState>
            </ContactState>      
        </AuthState>
    )
}

export default App

here is when my state looks like before I register/login a user.

[
  {
    "name": "Reducer",
    "value": {
      "token": null,
      "isAuthenticated": null,
      "loading": true,
      "error": null,
      "user": null,
      "loggedIn": false
    },
    "subHooks": [],
    "hookSource": {
      "lineNumber": 1715,
      "functionName": "AuthState",
      "fileName": "http://localhost:4445/static/js/bundle.js",
      "columnNumber": 78
    }
  }
]

Here is my state after registering/logging in a user

[
  {
    "name": "Reducer",
    "value": {
      "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjp7ImlkIjoiNjI4YmZjZGYyMTE1MDFhMmJmYjViYzU0In0sImlhdCI6MTY1MzM0MTQwOCwiZXhwIjoxNjUzNzAxNDA4fQ.9O6Loe_8gSEXJ5k0xZP-J4f5LtlDYKnSmwmC2HFJTrA",
      "isAuthenticated": true,
      "loading": false,
      "error": null,
      "user": "{user: {…}}",
      "loggedIn": true
    },
    "subHooks": [],
    "hookSource": {
      "lineNumber": 1715,
      "functionName": "AuthState",
      "fileName": "http://localhost:4445/static/js/bundle.js",
      "columnNumber": 78
    }
  }
]

Upon refreshing the page, this is state

[
  {
    "name": "Reducer",
    "value": {
      "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjp7ImlkIjoiNjI4YmZjZGYyMTE1MDFhMmJmYjViYzU0In0sImlhdCI6MTY1MzM0MTQwOCwiZXhwIjoxNjUzNzAxNDA4fQ.9O6Loe_8gSEXJ5k0xZP-J4f5LtlDYKnSmwmC2HFJTrA",
      "isAuthenticated": null,
      "loading": true,
      "error": null,
      "user": null,
      "loggedIn": false
    },
    "subHooks": [],
    "hookSource": {
      "lineNumber": 1715,
      "functionName": "AuthState",
      "fileName": "http://localhost:4445/static/js/bundle.js",
      "columnNumber": 78
    }
  }
]

In my Applications tab, the token is still present in localStorage, but the user is logged out.

Key: Token
value: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjp7ImlkIjoiNjI4YmZjZGYyMTE1MDFhMmJmYjViYzU0In0sImlhdCI6MTY1MzM0MTQwOCwiZXhwIjoxNjUzNzAxNDA4fQ.9O6Loe_8gSEXJ5k0xZP-J4f5LtlDYKnSmwmC2HFJTrA

Any help would be greatly appreciated, I've been working on this for about 4 days and cannot understand exactly where I'm going wrong.



Sources

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

Source: Stack Overflow

Solution Source