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.
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
.
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.
- Using a callback passed to
setState
. - Using
componentDidUpdate
life cycle method
Let’s go over them both.
1. Using a callback passed to setState
setState
has the following signature.
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.
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
.
And this demo shows that componentDidUpdate
- has the access to the updated state value.
- 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.
The post Accessing React State right after setting it appeared first on Sung's Technical Blog.
Latest comments (34)
Very helpful!
I have no words to thank you for this. I am a beginner in React and literally due to this particular issue, I was stuck for almost half a day. I used to think stuff involving API calls would be async, but I suppose there's more than just that. After all, I came to know this now. Great article! 🦄🚀
Thank you! Reaching prevState from componentDidUpdate solved my preoblem.
I just created an account to thank you for this article.
Thank you, Khaled :)
And welcome to DEV~ 👋
I create an account just to say thank you. The callback from the sestate worked for me.
Welcome to DEV André 👋.
You're welcome & thank you for taking time for the reply, as it means much to me :)
As aside note, using hooks, a similar way is to accomplish is to use
useEffect
(as you do forcomponentDidUpdate
).e.g.)
This blog post has saved me like 3 times now :D
Woohoo great to hear that the post was helpful 😄
Let's hope together and edge case like this can be handled without much thought 😎
interesting post. My favorite solution is to use requestAnimationFrame for the block that want to receive the new state. I usually limit usage of componentDidUpdate in this case since it can cause forever loop if there is any state update in componentDidUpdate
Funny thing is, I haven't ran across that error yet because I normally use
componentDidUpdate
for those state changes. Tutorials I followed taught my well I guess... hahaha. +10pts for that online editor you're using :pSometimes state changes from higher order components or from implicit state changes so you use
componentDidUpdate
to properly catch the new state and perform some additional logic.Thanks for the article! I just had this problem yesterday. It took a while to figure out that the problem was setState and not the helper function I was running just ahead of it but I eventually realized my error. I used a callback instead of the lifecycle method because it was all part of a form submit. Hmmm, I just realized there's a better way... Off to improve my code!
You're welcome there Andie.
But be aware of
componentDidUpdate
causing an infinite recursion as Truong pointed out.You can read it in the reactjs documentation, it says:
You may call setState() immediately in componentDidUpdate() but note that it must be wrapped in a condition like in the example above, or you’ll cause an infinite loop.
The example is
In my case, i update and re-render component whenever i receive new data from websocket.