Better ReactJS patterns: this.setState pitfalls
Tags: javascript, frontend, webdev
Published: 2017-10-23 21:26:44

---
title: "Better ReactJS patterns: this.setState pitfalls"
published: true
description: Passing setState an Object literal can lead to some unintended consequences. Get to know them.
tags: javascript, frontend, webdev
cover_image: https://thepracticaldev.s3.amazonaws.com/i/i38k4wddq8tp84icz3ke.PNG
---

There's a potential problem with passing object literals to setState like below,

```javascript
this.setState({someKey: someValue})
```
The code snippet below illustrates the potential problem. We called setState three times in quick succession, and added a callback to log the updated state to the console after each call.
```javascript

state = {
  counter: 0
}

incrementCounter = () => {
  this.setState(
    {
      counter: this.state.counter + 1
    },
    () => console.log()
  )
}

componentDidMount = () => {
  incrementCounter()
  incrementCounter()
  incrementCounter()
}

//output

{counter: 1}
{counter: 1}
{counter: 1}


```

You might have expected the output to be:
```javascript
{counter: 1}
{counter: 2}
{counter: 3}
```
There are two reasons for the unintended output:
1. Asynchronous updates
2. Batched updates

Reacts asynchronous update can best be described with the code snippet below:

```javascript
state = {
  counter: 0
}

incrementCounter = () => {

  this.setState(
    {
      counter: this.state.counter + 1
    }
  )

  console.log(this.state.counter) //this will always return the state before the new state above is reflected in state
}

incrementCounter() // 0
incrementCounter() // 1
```
Instead of logging 1, the initial call to incrementCounter logs 0, second call logs 1 instead of 2, and it continues like that.

Batched updates is described in the official docs with the code sample below,

```javascript
Object.assign(
  previousState,
  {quantity: state.quantity + 1},
  {quantity: state.quantity + 1},
  ...
)
```

So our initial code snippet is actually transformed into something like this,
```javascript
Object.assign(
  previousState,
  {counter: state.counter + 1},
  {counter: state.counter + 1},
  {counter: state.counter + 1})
```

So how do you avoid these potential issues, by passing a function to setState rather than an Object.

```javascript
incrementCounter = () => {
  this.setState((presentState) => (
    Object.assign({}, presentState, {
      counter: presentState.counter + 1
    })
  ))
}

componentDidMount = () => {
  incrementCounter()
  incrementCounter()
  incrementCounter()
}

//output

{counter: 3}
{counter: 3}
{counter: 3}
```

This way, the setState method will always pass an up-to-date state to the function. Notice that we use Object.assign to create a new object from the presentState.

Note that you shouldn't do this,

```javascript
this.setState((presentState) => {
  presentState.counter+= 1
  return presentState
})

```
Though the above will cause an update to state and re-render, the snippet below won't, due to React's shallow comparison.

```javascript
state = {
  someProp: {
    counter: 0
  }
}
this.setState((presentState) => {
  presentState.someProp.current += 1
  return presentState
})
```

It's still safe to pass setState an object literal when the new state doesn't depend on the old state, but passing it functions instead is a better pattern. If you are familiar with Redux, it's similar to Redux's reducers.

You might have noticed my use of arrow functions with incrementCounter method. It's the proposed es7 [property initializer syntax](https://github.com/tc39/proposal-class-public-fields), you can use it now with the babel [transform-class-properties plugin](https://babeljs.io/docs/plugins/transform-class-properties/).

Cheers.