Back

Technologies:

javascriptjavascript
reactjsreactjs
node.jsnode.js
avatar
Tolerim
15 days ago

The user is receiving a null value upon page refresh.

As a new React user and Stack Overflow member, I apologize in advance for any mistakes I may make. After successfully retrieving the user from local storage when redirected to the home page, I am encountering an issue when attempting to redirect to another page or refresh the page: the user is null initially, and then the user is retrieved. What is causing this issue, and how can I retrieve the user on the first attempt? Below are the codes I'm using: In AuthContext.js:


import { createContext, useReducer, useEffect } from "react";

export const AuthContext = createContext();

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

export const AuthContextProvider = ({ children }) => {
  const [state, dispatch] = useReducer(authReducer, {
    user: null,
  });

  useEffect(() => {
    const user = JSON.parse(localStorage.getItem("user"));

    if (user) {
      dispatch({ type: "LOGIN", payload: user });
    }
  }, []);

  console.log("AuthContext state:", state);

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

In useAuthContext.js:
html

import { AuthContext } from "../context/AuthContext";
import { useContext } from "react";

export const useAuthContext = () => {
  const context = useContext(AuthContext);

  if (!context) {
    throw Error("useAuthContext must be used inside an AuthContextProvider");
  }

  return context;
};

In useLogin.js: ```html

import { useState } from "react";
import { useAuthContext } from "./useAuthContext";

export const useLogin = () => {
  const [error, setError] = useState(null);
  const [isLoading, setIsLoading] = useState(null);
  const { dispatch } = useAuthContext();

  const login = async (email, password) => {
    setIsLoading(true);
    setError(null);

    const response = await fetch("http://localhost:7000/api/user/login", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ email, password }),
    });
    const json = await response.json();

    if (!response.ok) {
      setIsLoading(false);
      setError(json.error);
    }
    if (response.ok) {
      // save the user to local storage
      localStorage.setItem("user", JSON.stringify(json));

      // update the auth context
      dispatch({ type: "LOGIN", payload: json });

      // update loading state
      setIsLoading(false);
    }
  };

  return { login, isLoading, error };
};

When I am logged in and redirected to the home page, the user is successfully retrieved from local storage. However, when I attempt to redirect to another page or refresh the page, the user is initially null and is then retrieved. How can I retrieve the user from local storage on the first attempt instead of the second attempt? The codes I have used are displayed above.
Answers(1)
avatar
Tolerim
15 days ago
Verified Answer
The issue you are facing is because the useEffect hook in your AuthContext.js file is only called once when the component mounts. This means that when you refresh the page or navigate to another page, the component is remounted and the useEffect hook is called again, which results in the user being null initially. The code is working as expected as it's getting the user from localStorage, but it’s happening asynchronously which is why it appears as though the user is null at first. To solve this problem, you can modify the AuthContext.js code to check for the user in localStorage before rendering the component and pass that as the initial value to the useReducer hook. This way, even if the component is re-rendered or re-mounted, the user won't be null and will be available immediately. Here's an updated version of AuthContext.js:
import { createContext, useReducer, useEffect } from "react";

export const AuthContext = createContext();

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

const getUserFromLocalStorage = () => {
  const user = JSON.parse(localStorage.getItem("user"));

  if (user) {
    return { user };
  }

  return { user: null };
};

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

  useEffect(() => {
    // previous code to get the user from local storage is no longer required
  }, []);

  console.log("AuthContext state:", state);

  return (
    <AuthContext.Provider value={{ ...state, dispatch }}>
      {children}
    </AuthContext.Provider>
  );
};
Additionally, you can remove the console.log statements in your code to avoid cluttering the console.
;