简体   繁体   中英

Spread operator, setState - React

I have a state with validations like this:

this.state = {
        email: '', pass: '',
        errors: {
            email: '', pass: ''
        }
    }

and when a user press a button, it validates the form like this:

    onSubmit = (e) => {
    e.preventDefault();

    this.setState({errors: {pass: '', email: ''}});

    const pass = this.state.pass.trim();

    if(!isEmail(this.state.email)){
        this.setState({errors: {...this.state.errors, email: 'Invalid Email.'}});
    }

    // return; - in this way email state error will be ok

    if(pass.length < 6){
        this.setState({errors:{ ...this.state.errors, pass: 'Password Invalid'}});
    }

    return console.log(this.state);
};

I am using ... operator to append type of errors in the errors object. The problem is that, if the email is invalid and there is an error for the password the state is like this:

errors: {email: '', pass: 'Password Invalid'}

If I use return before pass check, the email error state works normal. I know that setState is async, but how to append different values of deep state object ?

Just setState after you've completely created your errors object. You're setting state more than you need to. You're only really interested in setting the state in this function once you know what all the errors are.

onSubmit = (e) => {
    e.preventDefault();
    const pass = this.state.pass.trim();
    let errors = { pass: '', email: '' };
    if (!isEmail(this.state.email)) {
        errors.email = 'Invalid Email.';
    }
    if (pass.length < 6) {
        errors.pass = 'Password Invalid';
    }
    this.setState({ errors });
}

Using the ... operator here was not the issue. What was likely occurring was that React was bundling your updates together and so each of them was looking at the same state. So if you have the state as

{
    error: {
        email: '',
        pass: '',
    }
}

and you have two updates batched together

setState({errors: {...this.state.errors, email: 'Invalid Email.'}});
setState({errors:{ ...this.state.errors, pass: 'Password Invalid'}});

they will both see the same state and make updates accordingly to produce the following two state updates

this.state = {
    error: {
        email: 'Invalid Email',
        pass: '',
    }
};
this.state = {
    error: {
        email: '',
        pass: 'Password Invalid',
    }
};

of which the second is most likely to be your result because it's the last one that was assigned to the state.

With state function you can do it:

this.setState(prevState => {
   const errors = {};

    if(!isEmail(prevState.email)){
        errors.email = 'Invalid Email.';
    }

    if(prevState.pass.trim().length < 6) {
        errors.pass = 'Password Invalid'
    }

    return {errors: {...prevState.errors, ...errors}}; // combine your new errors with another keys errors if exists
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM