1

I need help understanding why is that the state is updated in the React Developer Tools state, but not when i console.log it. Below is the code:

const [values, setValues] = useState({
    lastName: "",
    firstName: "",
    lastNameHiragana: "",
    firstNameHiragana: "",
    birthday: "",
    sex: "男",
    telephone: "",
    email: "",
    enterDate: "",
    companyId: "securityInt(SECI)",
    departmentId: "dev",
    commissionStatusId: "somestring",
    houseStatusId: "somestring",
    businessManager: "",
  });


  const onChange = (e) => {
    setValues({ ...values, [e.target.name]: e.target.value });
  };

  const handleForm = (e) => {
    e.preventDefault();

    const data = {
      firstName: values.firstName,
      lastName: values.lastName,
      firstNameHiragana: values.firstNameHiragana,
      lastNameHiragana: values.lastNameHiragana,
      companyId: values.companyId,
      birthday: values.birthday,
      sex: values.sex === "somestring" ? 0 : 1,
      mail: values.email,
      telephone: values.telephone,
      enterDate: values.enterDate,
      departmentId: values.departmentId,
      commissioningStatusId: values.commissionStatusId === "somestring" ? 0 : 1,
      houseStatusId: values.houseStatusId,
      businessManager: values.businessManager,
    };

    const newErrorMessage = {};
    const refresh_token = localStorage.getItem("refresh_token");
    const headers = {
      headers: {
        Authorization: `Bearer ${refresh_token}`,
      },
    };

    for (const name in values) {
      const hiraganaRegex = /^[ぁ-ん]+$/;
      const telephoneRegex = /^[0-9]{9,11}$/;
      const emailRegex = /^[a-z0-9._%+-]+@[a-z0-9.-]+.[a-z]{2,4}$/;

      switch (name) {
        case "lastName":
        case "firstName":
        case "birthday":
        case "enterDate":
          if (!values[name]) {
            newErrorMessage[name] = "必須項目です";
          }
          break;

        case "lastNameHiragana":
        case "firstNameHiragana":
          if (!values[name]) {
            newErrorMessage[name] = "必須項目です";
          } else if (!hiraganaRegex.test(values[name]) && values[name]) {
            newErrorMessage[name] = "ひらがなで入力して下さい。";
          }
          break;

        case "telephone":
          if (!values[name]) {
            newErrorMessage[name] = "必須項目です";
          } else if (!telephoneRegex.test(values[name]) && values[name]) {
            newErrorMessage[name] = "9~11桁の半角数字で入力して下さい";
          }
          break;

        case "email":
          if (!values[name]) {
            newErrorMessage[name] = "必須項目です";
          } else if (!emailRegex.test(values[name]) && values[name]) {
            newErrorMessage[name] = "メールアドレスの形式が正しくありません";
          }
          break;
      }
    }
    if (!Object.keys(newErrorMessage).length) {
      // Get companies ID
      const fetchData = async () => {
        if (
          typeof values.companyId === "string" &&
          typeof values.departmentId === "string"
        ) {
          const [companyRes, departmentRes] = await Promise.all([
            axiosInstance.get("/company"),
            axiosInstance.get("/department"),
          ]);

          const company = values.companyId.slice(
            0,
            values.companyId.indexOf("(")
          );
          const findCompany = companyRes.data.result.find(
            (item) => item.name === company
          );
          const findDepartment = departmentRes.data.result.find(
            (item) => item.name === values.departmentId
          );
          console.log(values.companyId);
          console.log(values);
          setValues((prevValues) => ({
            ...prevValues,
            companyId: findCompany.id,
            departmentId: findDepartment.id,
          }));
          console.log(values.companyId);
        }
      };

      // Send the information to server
      const sendData = async () => {
        try {
          const response = await axiosInstance.post("/employee", data, {
            headers: headers,
          });

          console.log(response);
        } catch (error) {
          console.log(error);
        }
      };

      // Call the functions in sequence
      (async () => {
        await fetchData();
        await sendData();
      })();
    }

    setErrorMessage(newErrorMessage);
  };

I need to send the correct companyId and departmentID, which is OK when I check React Developer Tools state, but when I console.log the values, it gives me not the desired result. Is the below not working, why?

setValues((prevValues) => ({
       ...prevValues,
       companyId: findCompany.id,
       departmentId: findDepartment.id,
}));

Is there anything wrong with the code? Any input would be helpful.

Thank you in advance.

raio
  • 67
  • 5

2 Answers2

0

The “useState” set method is asynchronous, hence, the updates are not reflected immediately. You can read this for more clarity.

Since you are using hooks, in your case you need to useEffect hook to see the actual value in the state. You can follow this answer

Rohan Sharma
  • 324
  • 3
  • 12
  • Hello Rohan. Thank you for the help. About using useEffect, i tried but it throws an error: React Hook "useEffect" is called in function "handleForm" that is neither a React function component nor a custom React Hook function. – raio Jun 14 '23 at 19:03
  • ```useEffect(() => { console.log(values.companyId); }, [values.companyId]);``` should be on the same level as your `useState`, `onChange` and `handleForm` functions. You cannot put `useEffect` inside the `handleForm` function. [Read this](https://legacy.reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level). – Rohan Sharma Jun 15 '23 at 08:43
0

In React, the setValues function from useState is asynchronous, meaning that it schedules the state update to occur on the next render, not instantaneously. Therefore, if you attempt to log the state immediately after updating it with setValues, you're likely to encounter the previous state instead of the updated one.

In the React Developer Tools, however, the state is displayed after all updates and re-renders have been completed. Thus, even though console.log may show the previous state, the React Developer Tools correctly displays the updated state.

This behavior can be observed in your code:

setValues((prevValues) => ({
    ...prevValues,
    companyId: findCompany.id,
    departmentId: findDepartment.id,
}));
console.log(values.companyId);

Here, console.log(values.companyId); is executed before the state has been updated, due to the asynchronous nature of setValues.

To tackle this situation, you can use the useEffect hook, which enables side effects in function components and performs an action when a specific value changes. Here's how you might use it:

useEffect(() => {
  console.log(values.companyId);
}, [values.companyId]); // This effect runs whenever `values.companyId` changes

This useEffect hook will log companyId whenever it changes, thus always reflecting the most recent value.

Furthermore, your sendData function is utilizing the data object which was defined before fetchData was invoked and the state was updated. You should define data inside the sendData function to ensure that the updated state is used:

const sendData = async () => {
  const data = {
    firstName: values.firstName,
    lastName: values.lastName,
    firstNameHiragana: values.firstNameHiragana,
    lastNameHiragana: values.lastNameHiragana,
    companyId: values.companyId,
    birthday: values.birthday,
    sex: values.sex === "somestring" ? 0 : 1,
    mail: values.email,
    telephone: values.telephone,
    enterDate: values.enterDate,
    departmentId: values.departmentId,
    commissioningStatusId: values.commissionStatusId === "somestring" ? 0 : 1,
    houseStatusId: values.houseStatusId,
    businessManager: values.businessManager,
  };

  try {
    const response = await axiosInstance.post("/employee", data, {
      headers: headers,
    });

    console.log(response);
  } catch (error) {
    console.log(error);
  }
};

Now, if you place the data object inside the sendData function, it will always use the current state when the function is called.

However, be aware that due to the asynchronous nature of the useState hook, the state may not be updated immediately when sendData is called, which might lead to the data object not having the updated values.

A better approach would be to return the updated state from the fetchData function and pass it as an argument to the sendData function. Here's how to do it:

const fetchData = async () => {
  if (
    typeof values.companyId === "string" &&
    typeof values.departmentId === "string"
  ) {
    const [companyRes, departmentRes] = await Promise.all([
      axiosInstance.get("/company"),
      axiosInstance.get("/department"),
    ]);

    const company = values.companyId.slice(
      0,
      values.companyId.indexOf("(")
    );
    const findCompany = companyRes.data.result.find(
      (item)

 => item.name === company
    );
    const findDepartment = departmentRes.data.result.find(
      (item) => item.name === values.departmentId
    );

    const newValues = {
      ...values,
      companyId: findCompany.id,
      departmentId: findDepartment.id,
    };

    setValues(newValues);

    return newValues;
  }
};

const sendData = async (data) => {
  try {
    const response = await axiosInstance.post("/employee", data, {
      headers: headers,
    });

    console.log(response);
  } catch (error) {
    console.log(error);
  }
};

(async () => {
  const newValues = await fetchData();
  sendData(newValues);
})();

In this adjusted code, newValues stores the updated state which we pass directly to sendData, ensuring that sendData uses the correct, updated state.

neo-jgrec
  • 167
  • 8
  • Hey man, thank you for the explanation. Very helpful. Regarding your statement: >Additionally, your sendData function is using the data object that was defined before fetchData was called and the state was updated. You should move the definition of data into the sendData function to ensure it uses the updated state: I did not get the updated state. Anyway you would know why? About using ```useEffect```, i tried but it throws an error: React Hook "useEffect" is called in function "handleForm" that is neither a React function component nor a custom React Hook function. – raio Jun 14 '23 at 18:50
  • You are right, we cannot use hooks inside event handlers (like the `handleForm` function). Hooks can only be called at the top level of your functional component. So using `useEffect` inside `handleForm` would lead to an error. We could resolve this issue would be to pass the updated state as an argument to the `sendData` function, right after setting it. – neo-jgrec Jun 14 '23 at 20:26
  • How does one do that, my friend? – raio Jun 14 '23 at 20:48
  • I have edited my answer, may this help you @raio – neo-jgrec Jun 15 '23 at 15:57
  • Thank you a lot. You helped me resolve the problem, and everything works fine. Keep up the goodness of helping others. – raio Jun 18 '23 at 08:40