33

How can I use shouldComponentUpdate for states?

I can check:

shouldComponentUpdate(nextProps, nextState) {
  return this.state.value != nextState.value;
}

But it doesn't have any sense for states. Because if I change state(this.setState({value: 'newValue'})) this.state will be equal nextState.

For example, onClick event:

handleClick() {
  this.setState({value: 'newValue'});
}
Nick
  • 343
  • 1
  • 3
  • 7
  • the components should simply rerender when its state changes, I thought the [`shouldComponentUpdate`](https://facebook.github.io/react/docs/component-specs.html#updating-shouldcomponentupdate) is mainly for the props, not the states. From the [specs](https://facebook.github.io/react/docs/component-specs.html#updating-shouldcomponentupdate) you can see that you can decide wether the new state warrants a component update, for example, you might have logic that doesn't need to update the current state, but you cannot compare it with the previous state as you can with props – Icepickle Oct 07 '16 at 23:27
  • @Icepickle, In my case I want to compare states because my component doesn't get `props` but a parent component re-render it. – Nick Oct 07 '16 at 23:40
  • Compare states and decide whatever you'd like update it in the place where you actually change it – klimat Oct 08 '16 at 00:13

1 Answers1

33

The shouldComponentUpdate(nextProps, nextState) method works for both props and state. In your example, after the onClick event the following method is fired by React.

shouldComponentUpdate(nextProps, nextState) {
  return this.state.value != nextState.value;
}

The key here is that in the above method the value of this.state.value is equal to what it was before the setState() call. This is thanks to the fact that in React:

setState() does not immediately mutate this.state but creates a pending state transition.
React docs: https://facebook.github.io/react/docs/component-api.html#setstate

Have a look at this demo: http://codepen.io/PiotrBerebecki/pen/YGZgom (full code below)

React keeps on state the count of every click on the button and also saves the randomly picked value (true or false). However thanks to the shouldComponentUpdate method, the component is re-rendered only if the previous value is not equal to upcoming / new value. This is why the displayed click count sometimes skips rendering its current state. You can comment out the whole shouldComponentUpdate method to re-render on every click.

class App extends React.Component {
  constructor() {
    super();
    this.state = {
      value: true,
      countOfClicks: 0
    };
    this.pickRandom = this.pickRandom.bind(this);
  }

  pickRandom() {
    this.setState({
      value: Math.random() > 0.5, // randomly picks true or false
      countOfClicks: this.state.countOfClicks + 1
    });
  }

  // comment out the below to re-render on every click
  shouldComponentUpdate(nextProps, nextState) {
    return this.state.value != nextState.value;
  }

  render() {
    return (
      <div>
        shouldComponentUpdate demo 
        <p><b>{this.state.value.toString()}</b></p>
        <p>Count of clicks: <b>{this.state.countOfClicks}</b></p>
        <button onClick={this.pickRandom}>
          Click to randomly select: true or false
        </button>
      </div>
    );
  }
}

ReactDOM.render(
  <App />,
  document.getElementById('app')
);
Piotr Berebecki
  • 7,428
  • 4
  • 33
  • 42
  • 1
    Thanks. That's strange because I set 'value: ''' in `constructor`. And after `onClick` event I get `this.state.value` is `newValue` in `shouldComponentUpdate`. But I thought it should be `this.state.value = '', nextState.value = 'newValue'`. – Nick Oct 08 '16 at 18:27
  • Have a look at the console output here: http://codepen.io/PiotrBerebecki/pen/LRdppQ. The initial value is `0` and therefore when you click the button `this.state.countOfClicks` is `0` and `nextState.countOfClicks` is `1`. You can also see that inside `shouldComponentUpdate` `this.state.countOfClicks` is always lagging behind what is actually rendered on screen. Does it make sense? – Piotr Berebecki Oct 08 '16 at 18:54
  • 2
    Yes, I tested your example. It works. But in my real project `this.state` is equal to `nextState`. That's strange. Keep looking. Maybe I did something wrong. Thank you. – Nick Oct 09 '16 at 17:15
  • You may be mutating the state directly. Here are some hints: https://github.com/facebook/react/issues/1388 – Piotr Berebecki Oct 09 '16 at 18:49
  • 1
    Yes, thanks. I change state before I call `setState`. `let state = this.state; state.value = 'newValue'; this.setState(state)`; – Nick Oct 10 '16 at 18:48
  • 1
    Instead of the above, update your state in the following way: `this.setState({ value: 'newValue' });` That's it. Here is a simple demo: http://codepen.io/PiotrBerebecki/pen/GjdgJK – Piotr Berebecki Oct 10 '16 at 20:55
  • This is a great example! – Si8 Aug 29 '19 at 14:00