Search code examples
javascriptreactjsuse-effect

UseEffect showing inappropriate value, but only once


I am trying to print the value of the state value whenever I change the password (using useEffect hook). Although it's working well, whenever I try to change the email, the value of email is also rendering in the console

useEffect(() => {
    console.log(values);
  }, [values.password]);

but as per my logic should be only rendered whenever the value of password is changed.

Following is the log enter image description here

As I marked they must not be shown as they are rendering whenever I change the value of email

Following is my code

Form.js

import { useState } from "react";

export const useForm = (initialValues) => {
  const [values, setValues] = useState(initialValues);

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

App.js

import "./App.css";
import { useState, useEffect } from "react";
import { useForm } from "./Form";

const App = () => {
  const [values, handelChange] = useForm({ email: "", password: "" });

  useEffect(() => {
    console.log(values);
  }, [values.password]);

  return (
    <div className="field">
      <input
        type="email"
        name="email"
        value={values.email}
        onChange={handelChange}
      />
      <input
        type="password"
        name="password"
        value={values.password}
        onChange={handelChange}
      />
    </div>
  );
};

export default App;

Solution

  • The only thing you have to change is removing the commented values-destructoring at your useForm-hook:

      return [
        values,
        (e) => {
          setValues({
            ...values, // remove the comment from your code in the question!!
            [e.target.name]: e.target.value,
          });
        },
      ];
    };
    

    The comment causes, that password is removed (you can call the prop password on values, but you get undefined) from the new values-object on every email-input. In the log, you see that, but as you described, only once!

    Furthermore, I would change your useForm-hook to:

    const useForm = (initialValues) => {
      const [values, setValues] = useState(initialValues);
    
      return [
        values,
        (e) => {
          setValues(prevValues => {
            return {
              ...prevValues,
              [e.target.name]: e.target.value,
            }
          });
        }
      ];
    };
    

    If the new state is computed using the previous state, you should use the previous state from params. React state updates can be batched, and not writing your updates this way can lead to unexpected results.