简体   繁体   中英

React not rendering with correct state value after using this.setState

I am trying to build a component that automatically checks an input field for a valid email address, then adds it to an array using setState .

I have that part working correctly, but I also want it to remove the last item when you backspace on the empty field.

The problem I am having is that when I splice the last item in the array, and update it using setState , it doesnt render the new array value?

If I log the value in the function called by onKeyDown , it logs the correct updated array value of this.state.emails . The render function however logs the old value and the list doesnt update.

Codepen: https://codepen.io/adamcoulombe/pen/zJxoER

class MultiEmailInput extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            emails:[]
        };
    }
    fieldChange(e){
        if (e.target.value !== ''){
            if (this.validateEmail(e.target.value)===true){
                this.setState({emails: this.state.emails.concat(e.target.value)});

            }
        }
    }
    fieldKeyDown(e){
       // console.log(e.target.value);
        if(e.target.value===''){
            var key = e.keyCode || e.charCode;
            if (key == 8 || key == 46) {

                this.setState({ emails: this.state.emails.splice(-1) });
                //logs correct updated value
                console.log(this.state.emails);

            }
        }
    }
    validateEmail(email) {
        var re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
        return re.test(String(email).toLowerCase());
    }
  render() {
       //this value doesnt get updated?
      console.log(this.state.emails);
    return (
      <div className={"email-multi-input"+this.props.className}>
            <ul className="added-emails" ref="email-multi-input-added-emails">

                {

                    this.state.emails.map((email, i) => {
                    return (<li key={i}>{email}</li>)
                })}
            </ul>

            <input ref="email-multi-input-field" onChange={this.fieldChange.bind(this)} onKeyDown={this.fieldKeyDown.bind(this)} />
     </div>
    );
  }
}

React.render(
    <MultiEmailInput />, 
    document.body
);

The problem is with this line in fieldKeyDown :

this.setState({ emails: this.state.emails.splice(-1) });

the splice method mutates the array this.state.emails and removes the last element, however it returns that last element. So that line above sets this.state.emails equal to the last element of this.state.emails .

The reason you see the correct value when you call console.log(this.state.emails) in fieldKeyDown is because the effect of this.setState has not taken place yet since React does not update state immediately (See State Updates May Be Asynchronous ). So you are looking at the old this.state.emails , which has been mutated. When you console.log in render you see the new value because render is called after state has been updated.

An additional note: You should not mutate state directly (ie using splice ) instead you should only change state using setState . For this problem you can set this.state.emails equal to a slice of this.state.emails . Here's an example:

this.setState({ emails: this.state.emails.slice(0, -1) });

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