Mutating the state

React comes with a very clear and straightforward API to mutate the internal state of components. Using the setState function, we can tell the library how we want the state to be changed. As soon as the state is updated, React re-renders the component, and we can access the new state through the this.state property—that's it.

Sometimes, however, we could make the mistake of mutating the state object directly, leading to dangerous consequences for the component's consistency and performance.

First of all, if we mutate the state without using setState, two bad things can happen:

  • The state changes without making the component re-render
  • Whenever setState gets called in the future, the mutated state gets applied

If we go back to the counter example and change the click handler to the following, we can see that clicking + does not affect the rendered value in the browser. However, if we look into the component using the React DevTools, the value of the state is correctly updated. This is an inconsistent state and we surely do not want it in our applications:

  handleClick() { 
this.state.count++;
}

If you are doing it by mistake, you can easily fix it by using the setState API; but if you find yourself doing it on purpose, for example, to avoid the component re-rendering, you had better re-think the structure of your components.

As we saw in Chapter 3, Create Truly Reusable Components, one of the reasons why we use the state object is to store values that are needed inside the render method.

The second problem that occurs when the state is mutated directly is that, whenever setState is called in any other part of the component, the mutated state gets applied unexpectedly.

For example, if we keep on working on the Counter component and we add the following button, which updates the state creating a new foo property, we can see that clicking the + does not have any visible effect. However, as soon as we click Updatethe count value in the browser makes a jump, displaying the current hidden state count value:

  <button onClick={() => this.setState({ foo: 'bar' })}> 
Update
</button>

This uncontrolled behavior is something we want to avoid as well.

Last but not least, mutating the state has a severe impact on performance. To show this behavior, we are going to create a new component, similar to the list we used in Chapter 9Improve the Performance of Your Applications, when we learned how to use keys and PureComponent.

Changing the value of the state has a negative impact when using PureComponent. To understand the problem, we are going to create the following List:

  class List extends PureComponent

Inside its constructor, we initialize the list with two items and bind the event handler:

  constructor(props) { 
super(props);

this.state = {
items: ['foo', 'bar']
};

this.handleClick = this.handleClick.bind(this);
}

The click handler is pretty simple—it just pushes a new element into the array (we will see later why that is wrong) and then it sets the array back into the state:

  handleClick() { 
this.state.items.push('baz');

this.setState({
items: this.state.items
});
}

Finally, we use the render method to display the current length of the list and the button that triggers the handler:

  render() { 
return (
<div>
{this.state.items.length}
<button onClick={this.handleClick}>+</button>
</div>
);
}

Looking at the code, we might think that there are no issues; however, if we run the component inside the browser, we'll notice that the value doesn't get updated when we click +.

Even in this case, by checking the state of the component using the React DevTool, we can see how the state has been updated internally, without causing a re-render:

  <List>
State
items: Array[3]
0: "foo"
1: "bar"
2: "baz"

The reason why we experience this inconsistency is that we mutated the array instead of providing a new value.

Pushing a new item into the array, in fact, does not create a new array. The PureComponent decides if the component should be updated by checking whether the values of its properties and state are changed but, in this case, we passed the same array again. This can be counter-intuitive in the beginning, especially if you are not used to working with immutable data structures.

The point here is to always set a new value of the state property, which means we can easily fix the issue by changing the click handler of the List component in the following way:

  handleClick() { 
this.setState({
items: this.state.items.concat('baz')
});
}

The concat function of the array returns a new array, thus appending the new item to the previous ones. In this way, PureComponent finds a new array in the state and re-renders itself correctly.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.254.231