0

I'm trying to set validPassword and validFormData values to false whenever a form is submited and the values are empty. I'm calling this function on submit but it's only changing the last property in my useState

const [validPassword, setValidPassword] = useState({
    password: true,
    confirmPassword: true
})
const [validFormData, setValidFormData] = useState({
    email: true,
    name: true,
    lastName: true,
    phone: true,
 })

const signupAction = (e) => {
        e.preventDefault()
        if (formData.email === "" || formData.name === "" || formData.lastName === "" || formData.phone === "" || formData.password === "" || formData.confirmPassword === "") {
            for (let p in formData) {
                if (formData[p] === "") {
                    if (p === "password" || p === "confirmPassword") {
                        setValidPassword({ ...validPassword, [p]: false })
                    } else {
                        setValidFormData({ ...validFormData, [p]: false })
                    }
                }
            }
        } else {
            console.log('success')
        }
        /* signup(formData, history) */
    }

What I get from this is:

validPassword = {
  password: true,
  confirmPassword: false
}
validFormData = {
   email: true,
   name: true,
   lastName: true,
   phone: false,
}
Diego Rios
  • 463
  • 1
  • 5
  • 18
  • Do you have a example to reproduce the issue, in Codesandbox or fiddle? – Asutosh Aug 12 '20 at 13:45
  • Basically you are calling setState in a loop, which is a bit ugly. If posiible prepapre an Object inside the loop, once the loop is done assign the Object as state. – Asutosh Aug 12 '20 at 13:48
  • 1
    Use the functional form of the setter instead of the direct form using the current value: `setValidPassword(actualValidPassword => {/* Your function */})`. The committers are "lazy" and you will just be acting on the last value otherwise. – zero298 Aug 12 '20 at 13:48
  • Does this answer your question? [useState set method not reflecting change immediately](https://stackoverflow.com/questions/54069253/usestate-set-method-not-reflecting-change-immediately) – zero298 Aug 12 '20 at 13:50
  • 1
    Setting state in a loop is bad practice and it's expensive. Because for every setState the component will re render. you can create your data set in loop. And setState with those data after the loop. – Monzoor Tamal Aug 12 '20 at 13:51

1 Answers1

1

The setState function is async. The common pattern of setting a state which is the one you are using is not fool proof one. One should use callback based setting state to by pass these common issues. Code like below would work

const signupAction = (e) => {
    e.preventDefault()
    if (formData.email === "" || formData.name === "" || formData.lastName === "" || formData.phone === "" || formData.password === "" || formData.confirmPassword === "") {
        for (let p in formData) {
            if (formData[p] === "") {
                if (p === "password" || p === "confirmPassword") {
                    setValidPassword((prevState) => ({...prevState, [p]: false }))
                } else {
                    setValidFormData((prevState) => ({ ...prevState, [p]: false }))
                }
            }
        }
    } else {
        console.log('success')
    }
    /* signup(formData, history) */
}

Further, to prevent multiple updates, it is best if you store the changes in the loop in a local variable and set all at once like below

const signupAction = (e) => {
    e.preventDefault()
    if (formData.email === "" || formData.name === "" || formData.lastName === "" || formData.phone === "" || formData.password === "" || formData.confirmPassword === "") {
        const tempValidPassword = { ...validPassword};
        const tempValidFormData = { ...validFormData};
        for (let p in formData) {
            if (formData[p] === "") {
                if (p === "password" || p === "confirmPassword") {
                    tempValidPassword[p] = false;
                } else {
                    tempValidFormData[p] = false;
                }
            }
        }
        setValidPassword(tempValidPassword);
        setValidFormData(tempValidFormData);
    } else {
        console.log('success')
    }
    /* signup(formData, history) */
}

Note: These are only pesudo-code. Fit according to your needs. Try to use functional updates to state as mentioned here whenever possible

Panther
  • 8,938
  • 3
  • 23
  • 34