The Importance of React’s setState() Method in Class Components

Patrick Brennan
3 min readJan 27, 2022

--

State is the most important concept in React. State can store any mutable data; such as current user data, the number of likes on a post, etc. Part of what makes React such a useful tool is how performant and responsive it is to changes in state. With a single method, setState(), you can trigger a re-render with your updated state! Here is how it works:

Let’s say you have a component who’s express purpose is to increment a counter by one every time you click a button. First let’s create a ‘Counter’ class component, complete with state and a render method that returns a button and the current state. Let’s set the initial state to { counter: 0 }:

import React, { Component } from 'react';export default class Counter extends Component {
constructor() {
this.state = {
counter: 0
}
}
render() {
return (
<div>
<span>{this.state.counter}</span>
<button>Click Me!</button>
</div>
)
}
}

Now we need to create an event handler for the button. Within the handler we will invoke setState(), passing our state object with an updated value as our argument.

import React, { Component } from 'react';export default class Counter extends Component {
constructor() {
this.state = {
counter: 0
}
}
handleOnClick = (event) => {
this.setState({
counter: counter + 1
})
}
render() {
return (
<div>
<span>{this.state.counter}</span>
<button onClick={this.handleOnClick}>Click Me!</button>
</div>
)
}
}

Another approach you can take is passing an arrow function with the previous state as an argument.

handleOnClick = (event) => {
this.setState((previousState) => {
counter: previousState.counter + 1
})
}

This approach becomes particularly useful when dealing with batched updates. Part of the magic of setState() is that it runs asynchronously and bundles many updates together for increased performance. But if you have many things updating at the same time, you may end up with a render that does not truly represent current state. You can also pass props as well.

But all of this begs a question: why do it this way? Why not update state directly like so:

handleOnClick = (event) => { 
this.state.counter = this.state.counter + 1
}

or:

render() {
return (
<div>
<span>{this.state.counter}</span>
<button onClick={() => this.state.counter + 1}>
Click Me!
</button>
</div>
)
}

Because setState() triggers the reconciliation process. To discuss reconciliation, we’ll have to look under the hood of React a bit…

Part of what makes React so responsive is its use of the Virtual DOM — a virtual representation of the user interface kept in memory whose express purpose is to track and compare changes in state. When reconciliation is triggered, React compares the updated state with our previous state, creating a new virtual representation of the DOM as a new tree. Then, once all updates are processed, this tree in the Virtual DOM is used to update the DOM in our browser. So what would happen if we were to try updating state directly? Nothing, basically. The DOM would have no way of knowing that state has been updated.

So to wrap up, here are the three main takeaways:

  • When updating state in a class component, always use setState() to do so.
  • Using setState() triggers the reconciliation process, which updates state and triggers a re-render of the DOM, containing our newly updated state.
  • setState() batches updates together asynchronously to make React more performant.

--

--

Patrick Brennan

Charting my journey from coding noob to professional programmer.