user9623401
user9623401

Reputation:

use setState method correctly

I'm new to React, I was told by a book that it is incorrect to use setState method as the example below:

...
constructor(props) {
   super(props);
   this.state = {
       counter: 0,
          hasButtonBeenClicked: false
   }
}

render() {
 return (
 <button onClick={ this.handleClick }>
    Click
 </button>
 )
}

handleClick = () => {
   this.setState({ counter: this.state.counter + 1 });
   this.setState({ hasButtonBeenClicked: this.state.counter > 0 });
 }
...

becuase React performs changes to state data asynchronously and may choose to group together several updates to improve performance.

I get the point, but since React performs changes asynchronously . there could be times and chances that the first setState method will get called first before the second setState method, it is just a matter of probability. But no matter how many times I tried, the second setState method always get called first, why?

Upvotes: 0

Views: 106

Answers (4)

bli07
bli07

Reputation: 673

You are right, React performs changes to state data asynchronously to improve performance. In your case, you should write code like this:

handleClick = () => {
  const counter = this.state.counter + 1;
  this.setState({
    counter: counter,
    hasButtonBeenClicked: counter > 0
  });
}

Or, you can use callback parameter of setState.

Upvotes: 0

Dženis H.
Dženis H.

Reputation: 7822

If you have lots of states to update at once, group them all within the same setState:

Instead of:

this.setState({foo: "one"}, () => {
    this.setState({bar: "two"});
});

Just do this:

this.setState({
    foo: "one",
    bar: "two"
});

Or in your case:

handleClick = () => {
   this.setState({ 
   counter: this.state.counter + 1,
   hasButtonBeenClicked: this.state.counter > 0 
   });
}

Upvotes: 0

mbojko
mbojko

Reputation: 14669

  1. Don't reference this.state. Use callback variant of setState, get current state from there. That'll explicitly guard you against the "changed the state, but the state hasn't changed yet" situations.

And 2. Simply put those two updates into one run of setState:

this.setState((prevState) => ({
    counter: prevState.counter + 1,
    hasButtonBeenClicked: prevState.counter + 1 > 0 }));

Upvotes: 1

Gorbles
Gorbles

Reputation: 1168

Despite the fact that they're performed asynchronously, you're unlikely to see variance when testing from the same browser, and / or on the same hardware. Different browsers optimise instruction sets in slightly different ways, which is your best bet for examining performance (and why testing on all major desktop browsers is still recommended despite commercially Chrome often being "good enough").

The best way to do this - assuming you want them to fire in a specific order - is to chain the setState call as follows:

handleClick = () => {
    this.setState({ counter: this.state.counter + 1 }, () => {
        this.setState({ hasButtonBeenClicked: this.state.counter > 0 })
    });
}

But I would recommend a different design regardless - tying the hasButtonBeenClicked logic to the counter is going to introduce problems down the line.

Upvotes: 3

Related Questions