Accessing React State right after setting it

featured
August 25, 2018
💫 Originally posted here. Broken? Let me know ~

Photo by Celso on Unsplash

As I have been browsing Stack Overflow questions, I’ve noticed that many bugs are due to trying to access a state value after setting it.

An example question on Stack Overflow.

I’ve stumbled many times for being unaware of setState being an asynchronous operation.

How do we access the state value right after setting it then?

😬 Reproducing the Problem

Here is the code that shows accessing a state value (clickCounts) right after setting it synchronously.

View Gist on GitHub

And let’s see the logical error.

console.log doesn’t have access to updated state value even though the call is made after setState.

😒  Workaround (Not Recommended)

As setState is an operation, you can just wait till the value is set by React.

You might wait for a certain period to access the updated state using setTimeout.

View Gist on GitHub

Tada 🎉. It works right?

Yes but No, at this point, you are just praying 🙏 that setState finishes before accessing the state within setTimeout.

And also, you need to persist the event to be able to access event argument as shown in line#2 (e.persist()).

Refer to Event Pooling for e.persist.

😄 Recommend Ways

There are two ways as mentioned in the official React documentation.

  1.  Using a callback passed to setState.
  2. Using componentDidUpdate life cycle method

Let’s go over them both.

1. Using a callback passed to setState

setState has the following signature.

View Gist on GitHub

The callback is called after the state has updated using updater method thus the callback has access to the updated this.state.

Here is the updated code & the demo.

onClick = e => {
e.persist();
const clickCounts = { ...this.state.clickCounts };
let clickCount = +clickCounts[e.target.name] || 0;
clickCounts[e.target.name] = ++clickCount;
// - From this,
// this.setState({ clickCounts });
// + To this
this.setState({ clickCounts }, () =>
console.log(
`Button Name (▶️️ inside callback) = `,
this.state.clickCounts
)
);
console.log(
`Button Name (◀️ outside callback) = ${e.target.name}`,
this.state.clickCounts
);
};

2. Using componentDidUpdate life cycle method

React documentation “generally recommends” using componentDidUpdate.

I haven’t been able to find the reason for it, but my guess is because componentDidUpdate has access to the previous props and previous state (as well as being called before the callback as my demo shows).

Here is the code using componentDidUpdate.

componentDidUpdate(prevProps, prevState) {
console.log(
`this.state.clickCounts(♻️ componentDidUpdate)`,
this.state.clickCounts
);
}

And this demo shows that componentDidUpdate

  1. has the access to the updated state value.
  2. is called before the setState’s callback method.

👋 Parting Words

Frankly speaking, I’ve only used the callback to access updated value because I only found out about the recommended way of using componentDidUpdate while writing this blog 😝.

And you can play around with the demo on CodeSandBox.