It might be unusual for new react developers or some experienced developers to see this explained. When we start learning react and start exploring how powerful it is. We consider this.setState as a very simple thing to understand and we don’t look deep into it. Most of us fell into that trap, resulting in unpredictable component state. Here are some of the common pitfalls that causes this.
this.setState() is an asynchronous function.
Well this might be surprise for some but, it’s well known for lot of us still we treat it like a synchronous function. Let’s start by what does setState do and why do we need it.
INTRODUCTION TO SETSTATE:
React Documentation and every other articles related to React mentions that you should never modify your state instead set it through setState. The reason for that is react doesn’t directly manipulate DOM. It has it’s own virtual DOM and it updates only the component that has some change in it, but there is slightly tricky part, It doesn’t update on any normal change, it only updates if there is change in state or props. To maintain consistency and predicted behavior of Components, Components trigger re-render only if it’s state is been set by using setState function.
So, basically this.setState sets a new object in existing state object ( to be more precise it merges two states).
So, if I change state multiple times from a single function, would it re-render the component again and again. That’s where the pitfall is. I will explain few scenario’s of how the value of state is set and becomes unpredictable over time and how we can avoid that with a simple technique.
In this case the value of counter is 1 at beginning after you set state to 2, the next line still has the value 1 because the state hasn’t been updated it. So, we should have in mind that the state is always the old one and this update doesn’t directly affect it. So, this looks simple and you might question the need of explanation. But next scenario will explain you why this matters.
Let’s continue with the example above, you now have in mind that the new state is not available in the next statement. But next statement is not always synchronous. See the example below:
An asynchronous function can be lot of things, here I am using setTimeout to mock the behavior. In this cases the state is already updated so that the next statement has a new value. This is very unpredictable because you never know how long it will take to update the state and will the next asynchronous function run before that or after that?
There might be certain cases in your project where you need to do multiple setState inside a single function. You should be aware of the value of state at different places. Here is an example:
In this case when you set state to increase the counter by 1 and again repeat the same operation. React doesn’t go and update the state directly and do the same for other. Actually it batches the setState and updates at once. However, if there is any other asynchronous function in between things change differently, You can understand that better from the scenario 2. Here is one more example:
This scenario occurs when people try to use callback inside setState without actually knowing how it behaves. The way we use callback in setState is:
Unlike normal setState the callback version doesn’t wait for component to re-render to retrieve the latest state value. Instead you can access the value of state directly before component re-renders.
This example looks pretty similar to scenario 3, but the difference here is prevState variable here is the updated state based on the statement above. Considering this example, the counter value is increased by 1, so it becomes 2 then the callback inside setState can access the new state set by previous statement (remember the main state hasn’t been updated yet and component hasn’t re-rendered). So the value of counter in new state should be 2 which when increased by 1 becomes 3. But the console.log below that statement still has the reference of state 2 modifications ago (because main state hasn’t been updated yet), so the value is still 1. And once setState is completed the component re-renders and the final state value is now 3.
While these all might seem pretty easy or confusing. You should always be careful of how the value of state changes based on the way you use this.setState inside your functions. This few scenarios should help you better predict the state of state ? at any point of time. You should also be careful on when you use callback to set State and when you just use a normal object. Let’s set the state of state with better predictability.
Note: A lot of things are changing in react. once async scheduling is activated by default in React 17, some of these issues may not be there and it will be more predictable.