Home > Mobile >  React Context API current
React Context API current

Time:11-27

Okay...what is happening here cause I don't undrestand? I have an react context api where I store the current user data. In the login function I console log the currentUser and everything looks fine, but then when I write the currentUser in chrome dev tools, it appears undefined. In localStorage I also have the data. What is happening? What am I doing wrong? Can someone, please help me.

enter image description here

Here is my code:

authContext.js

import { createContext, useState, useEffect } from "react";
import axios from "axios";

export const AuthContext = createContext();

export const AuthContextProvider = ({ children }) => {
  const [currentUser, setCurrentUser] = useState(
    JSON.parse(localStorage.getItem("user")) || null
  );

  const login = async (inputs) => {
    try {
      const res = await axios.post("/login", inputs);
      setCurrentUser(res.data);
      console.log("res.data: ", res.data); //returns data
      console.log("currentUser ", currentUser); //returns data
    } catch (error) {
      console.log(error);
    }
  };

  const logout = async () => {
    localStorage.clear();
    setCurrentUser(null);
  };

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

  return (
    <AuthContext.Provider value={{ currentUser, login, logout }}>
      {children}
    </AuthContext.Provider>
  );
};

index.js

import React from "react";
import ReactDOM from "react-dom/client";
import { AuthContextProvider } from "./ccontext/authContext";
import App from "./App";
import "./index.css";

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <React.StrictMode>
    <AuthContextProvider>
      <App />
    </AuthContextProvider>
  </React.StrictMode>
);

Login.jsx

/*...*/
const LoginForm = () => { 
  const [error, setError] = useState(null);
  const navigate = useNavigate();
  const { login } = useContext(AuthContext);

  const handleFormSubmit = (values, actions) => {
    try {
      login(values);
      actions.resetForm();
      navigate("/");
      console.log("logged in");
    } catch (err) {
      setError(err.response.data);
    }
  };
/*...*/

CodePudding user response:

Updating state is best seen like an asynchronous operation. You cannot set state in a function / effect and expect it to immediately be updated, on the spot. Well, it technically is, but you won't see the updates in your "already-running" function.

I am pretty sure that if you extract your log in the component root it will display the appropriate value after the login function finishes executing and the component properly sets the new state.

If you do need the value in the function you should directly use res.data.

A deeper dive:

Whenever your login function runs it forms a closure around your current values (including currentUser which is undefined at the moment).

When you update the currentUser in the login function you basically inform react that you need to update that value. It will handle this in the background, preparing the state for the next render, but your login function will keep running with whatever values it started with. Your "new" state values will not be available until you run the function again. This is because the already-running function "closed over" old values, so it can only reference those.

As a side note, if you use a ref for instance you would not have this problem. Why? Because refs do not participate in the react lifecycle. When you modify a ref it changes on the spot. You will have the updated value precisely on the next line. State does not work like that, it is coupled to the component lifecycle, so it will update on the next render.

  • Related