3

I am working on a login/register page in react and I use useState hooks for check if the password is strong or not. and display user what should he do to make his password stronger. But I noticed that when user is typing in the password field their is a delay in updating the password in the useState (in the console.log() of function handlePassword) . Therefore my function handlePassword is not working properly.

  const [err,setError]=useState("")
  const [password,setPassword]=useState("")

   function handlePassword(event){
      setPassword(event.target.value);
      if(password.length<6){
        console.log(password)
        setError("password should contain 6 character")
      }else if(!isInclude(password)){
         setError("password should contain a special character")
        
      }else{
        setError("")
      }
      

    }
   <input type="password" placeholder="password" required className="form-input" value={password} onChange={handlePassword} name="password"  onClick={clearInput}/>  

pawan gogoi
  • 41
  • 1
  • 3
  • Does this answer your question? [useState set method not reflecting change immediately](https://stackoverflow.com/questions/54069253/usestate-set-method-not-reflecting-change-immediately) – Brian Thompson Jun 01 '21 at 20:53
  • 1
    The value will not be update until the next render. This is how React state works. However, you already have the new value in `event.target.value` so just use it for the rest of your function. – Brian Thompson Jun 01 '21 at 20:54
  • 1
    Your other option would be to put the logic in a `useEffect` that listens to changes in `password`. Something like `useEffect(myFunc, [password])`. Then your validation function will only run after the state has been updated. – Brian Thompson Jun 01 '21 at 20:55

2 Answers2

4

There isn't a delay, setState works asyncronously, put your console.log outside of the function and you'll see the correct outcome. So for the same reason, you can't check the password length right after you set the state. Instead, you need to do that in a useEffect like this;

    useEffect(()=>{
       if(password.length<6){
        console.log(password)
        setError("password should contain 6 character")
        }else if(!isInclude(password)){
         setError("password should contain a special character")
        }else{
        setError("")
        }
             }, [password])
İlker
  • 1,872
  • 1
  • 12
  • 28
  • `[]` is the wrong deps list, you need to take `[password]` as a dep or the useEffect won't run. I also don't think useEffect is a great solution here, it's going to cause unnecessary double rendering, as the change to password will trigger a render, then the useEffect will trigger another render. – Retsam Jun 01 '21 at 20:57
  • I forgot to add the dependency @Retsam what is wrong with rendering the page one more time? also what is the solution? – İlker Jun 01 '21 at 21:05
  • the extra render is just a bit wasteful and in some cases can cause problems due to the intermediate state being unexpected and it might have a "flicker" effect as it changes between three states very quickly. You can see my answer, but I think it's better to either calculate the error from the password *during* the render (not as a state variable) or else the original approach of making two setState calls works. – Retsam Jun 02 '21 at 12:47
0

When you trigger a state change with react, it queues up a re-render with the new state. In the case of function components, this means the function will be called again with new values for all the state values.

But variables holding existing state values from the current render do not change. So password still holds the previous password's value, not the new one that was set by setPassword.

The short fix here is to use event.target.value instead of password when checking the length.


But it's probably best not to have error as its own state, instead I'd calculate it from the password:

const [password,setPassword]=useState("")
const error = password.length < 6 
    ? "password should contain 6 character" 
    : !isInclude(password)
    ? "password should contain a special character"
    : "";

This avoids the need to keep the two state variables in sync.

Retsam
  • 30,909
  • 11
  • 68
  • 90