Decoding the Delays: Understanding setState and How to Ensure Immediate Updates

Decoding the Delays: Understanding setState and How to Ensure Immediate Updates

With its continually updated features and extensive documentation, Reactjs has established itself as a leading library among developers worldwide. Its versatility allows for the creation of simple and complex web applications alike, solidifying its place as a go-to choice for millions of developers.

However, the setState function has been known to be a source of confusion for developers, especially people new to ReactJs. And yes, it makes sense because 'setState' is supposed to update the states within the component, which it does. But the real problem is, at what point is the updated state being used for some other operations? Because using the state immediately after it was updated will return the initial state value. Why is this so? In the following paragraphs, I explain why it is so and how you can ensure immediate updates in your application.

Why are React state updates delayed?

The setState function is asynchronous, which means that when the component encounters the function, it executes it but does not wait for the result before executing the next lines of code. For context, consider the example below.

Synchronous: Imagine you are preparing a multi-course meal and you start by making the appetizer. You are following a recipe and have all the ingredients and equipment ready. You start by sautéing the vegetables, then add the protein, and finally the sauce. Once the appetizer is ready, you serve it to your guests. Only after they have finished their appetizer, do you start preparing the main course. You have to wait for the guests to finish their appetizer before you can start preparing the main course. This is like a synchronous program, where one task is completed before the next task starts.

Asynchronous: Imagine you are preparing the same multi-course meal, but this time you start by preparing the appetizer and the main course at the same time. You start sautéing the vegetables for the appetizer and put it on the stove. At the same time, you start preparing the protein for the main course, and you also put it on the stove. While the appetizer and main course are cooking, you can also prepare the dessert, set the table, and take care of other things. Once the appetizer and main course are ready, you can serve them to your guests and continue with the next course. This is like an asynchronous program, where multiple tasks are happening at the same time and are completed independently of each other.

Given this understanding, the delay in getting the new value of the state immediately after setState has been called can now be understood. You may ask, "why not make it synchronous 🤷🏽‍♂️?" A well-documented response is provided in this RFC, and one of the main points is that ReactJs uses batching in order to limit the number of re-renders that occur due to state updates. Essentially, it aggregates the new values somewhere in the memory, then when it is done running and executing all the functions, it does a comparison of the props and states, and decides on what to re-render. This helps to optimize the performance of your application, and it also keeps the props and state consistent.

Ensure Immediate updates of your state

Despite the explanation provided, there may still be instances in your application when you need to access the updated value of your state immediately after setState has been called, in order to use that value in another component or for a computation within the same component. To address this, I will be outlining two methods for obtaining the current values below.

Passing function instead of objects into setState (Class component)

With this method, you can access the current state value inside the updater. And this allows you to use the value for whatever purpose you wanted to access it.

this.setState({value: "new value"}, () => {
   console.log(this.state.value) // "new value"
});

Using the useState and the useEffect hook (Functional component)

In a functional component, you can use the useState hook to manage the state and the useEffect hook to perform actions after the state has been updated.

const [value, setValue] = useState("initial value");

useEffect(() => {
  console.log(value);
}, [value]);

const handleClick = () => {
  setValue("new value");
};

return (
  <button onClick={handleClick}>Update value</button>
);

Something to note here is that once the useEffect notices a change in the value, the component is rendered, so you may want to be sure that the state update is important enough to necessitate a rerender, and this may be costly in some large applications.

Conclusion

It's important to note that the delay experienced when trying to access the new state immediately after setState has been called is not a bug, but rather a feature implemented by the React team. This is to ensure consistency within your application and to preserve its performance. If you must obtain the value of the state immediately, it's important to be aware of the trade-offs involved, especially in a functional component.

Thank you for reading this blog up to this point, I look forward to your comments and feedback 🙏🏽.