Prox4
Prox4

Reputation: 111

How to update state in react synchronously

onSave=()=>{
    if (this.state.intialValue<=0) {
        this.setState({errorIntialValue: true})
      }
      else
      {
      this.setState({errorIntialValue: false})
      }
      if (this.state.age>=25|| this.state.age<=-1) {
        this.setState({errorAge: true})
      }
      else{
        this.setState({errorAge: false})
      }
      if (this.state.rollNo<0) {
        this.setState({errorRollno: true})
      }
      else{
        this.setState({errorRollno: false})
      }
       if(!(this.state.errorIntialValue|| this.state.errorAge ||errorRollno)){    //have to 
                                                                    enter only if no error
    let newData={
            intialValue:this.state.intialValue,
            age:this.state.age,
            rollNo:this.state.rollNo
    }
    this.props.updateData(newData)
}

I have a onClick event onSave. If error is there in the form I'm setting the state of those to true.Since SetState is asynchronous,the value won't be updated to its state and is always undefined when it come to if(!(this.state.errorIntialValue || this.state.errorAge || errorRollno)) and it returns false. The code in if block will never get executed. I'm not able to find a proper way to achieve this.How can I do this?

Upvotes: 1

Views: 3026

Answers (3)

Stefan
Stefan

Reputation: 12390

await this.setState({ foo: this.state.foo + 1})

Upvotes: 0

Estus Flask
Estus Flask

Reputation: 223318

It's possible to use unstable_batchedUpdates as explained in this answer to make state updates synchronous:

// this.state.foo === 0 here

ReactDOM.unstable_batchedUpdates(() => {
    this.setState({ foo: this.state.foo + 1});
});

// this.state.foo === 1 here

This method is not applicable here, the need for it indicates that there is a problem.

The documentation suggests to use updater function if setState state depends on previous state, and use callback function if evaluated code depends on previously set state:

setState() does not always immediately update the component. It may batch or defer the update until later. This makes reading this.state right after calling setState() a potential pitfall. Instead, use componentDidUpdate or a setState callback (setState(updater, callback)), either of which are guaranteed to fire after the update has been applied. If you need to set the state based on the previous state, read about the updater argument below.

It's unclear from the code why temporary values (errorIntialValue, errorAge, errorRollno) should be stored in component state. They likely shouldn't and should be updated only once, something like:

if (errorIntialValue || errorAge || errorRollno) {
  // update the state with errors
} else {
  // update the state with data
}

Upvotes: 1

skyboyer
skyboyer

Reputation: 23763

As @BoyWithSilverWings pointed out it's better to use functional version of setState to be sure we run checks on stable state. Otherwise in case when you call this.onSave() programmatically you might operate on old version of state.

Having that in mind we utilize callback goes second parameter as @estus gave a link to.

onSave=()=>{
    this.setState(currentState = > {
        errorIntialValue: currentState.intialValue<=0,
        errorAge: currentState.age>=25|| currentState.age<=-1,
        errorRollno: currentState.rollNo<0
    }, ({errorIntialValue, errorAge, errorRollno}) => {
       if([errorIntialValue, errorAge, errorRollno].some(isInvalid=> isInvalid)){    
          let newData={
               intialValue:this.state.intialValue,
               age:this.state.age,
               rollNo:this.state.rollNo
          }
          this.props.updateData(newData)
       } 
    });

Upvotes: 0

Related Questions