Home > OS >  React : always the previous state id taken into account, even with prevState and useEffect
React : always the previous state id taken into account, even with prevState and useEffect

Time:10-03

I have a checkPassword function which checks the length of a password, this function is triggered "onChange" on the input of my password.

Here is the function :

 const checkPassword = () => {
    if (user_password.length >= 10)
      setPasswordFlag({ ...passwordFlag, length: true });
    else setPasswordFlag({ ...passwordFlag, length: false });
   };

And this is my password input :

<input type="password" className="input_container" placeholder="Mot de passe" id="password" name="password" onChange={(e) => {
   setUserSignup({
     ...userSignup,
     user_password: e.target.value,
   });
   checkPassword();
 }}
value={userSignup.user_password} ref={refSignupPassword} />

The problem is that each time, the previous state is taken into account. Basically my state passwordFlag.length changes to true at the 11th character instead of the 10th.

I tried using prevState, but it doesn't change anything.

Then ,i tried to use useEffect like this :

const checkPassword = () => {
      useEffect(() => {
        if (user_password.length >= 10)
          setPasswordFlag({ ...passwordFlag, length: true });
        else setPasswordFlag({ ...passwordFlag, length: false });
      }, []);
    };

But i have now this error : React Hook "useEffect" is called in function "checkPassword" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. I tried to rename my function with an uppercase, but sameproblem.

BUT if i put the checkPassword function in an onBlur={checkPassword}, then it works. But i need to fix it in "onChange" and not "onBlur"

Can someone tell me what I need to do to ensure that the correct state is taken into account?

Thank you !

CodePudding user response:

You cannot use "useEffect" inside a function blok.

You may change your function like this:

 const checkPassword = (password) => {
    if (password.length >= 10)
      setPasswordFlag({ ...passwordFlag, length: true });
    else setPasswordFlag({ ...passwordFlag, length: false });
   };

And your password input :

<input type="password" className="input_container" placeholder="Mot de passe" id="password" name="password" onChange={(e) => {
   setUserSignup({
     ...userSignup,
     user_password: e.target.value,
   });
   checkPassword(e.target.value);
 }}
value={userSignup.user_password} ref={refSignupPassword} />

We passed the value to the checkPassword function. Now your function can access the password length immediately.

CodePudding user response:

checkPassword Needs the next state value that you just set, but that wont; be available until the next render. This is because setting state triggers a new render, and in that next render the new state will be available. But that won't change the value of any local variables immediately.

You have at least two options here.


You could pass the password string directly to checkPassword, in the same way you do to setUserSignup

setUserSignup({
  ...userSignup,
  user_password: e.target.value,
});
checkPassword(e.target.value);

Then checkPassword doesn't read from any stale state.


Or create an effect that watches the setUserSignup value and sets some state if necessary.

Something like this:

useEffect(() => {
  const longEnough = user_password.length >= 10;
  if (passwordFlag.length !== longEnough) {
    setPasswordFlag({ ...passwordFlag, length: longEnough });
  }
}, [passwordFlag, setPasswordFlag]);

Note that it checks to see if the state needs updating before setting it. This prevents an infinite loop where an effect updates state, which runs an effect, which changes state, etc...

Note that effect goes in the main function of your component, and not in a any callback.

For example:

function MyComponent() {
  const [myState, setMyState] = useState() // setup you custom state here
  
  // effect that watches your state and then sets state if necessary here
  useEffect(() => {...}, [hook, dependencies, here])

  return <>...</>
}

Read the rules of hooks for more info.

  • Related