Editor’s note: This article was last updated on 12 December 2023 to further explore the asynchronous nature of React’s state updates.
Despite React’s popularity, one of its biggest drawbacks is the excessive re-rendering of its components. When developing React applications, you may have noticed that state updates don’t immediately reflect new values after being changed. React state is a plain JavaScript object that holds information that influences the output of a render.
When building your project, if you intend to alter any attributes of a React component in the future, you should store the attribute in a state. The state starts with an initial default value on mount
and is then later altered as a result of a user’s actions. Each React component manages its own state internally.
In this article, we’ll explore the reasons why React doesn’t update state immediately. We’ll run through an example and clarify what you should do when you need to make changes to the new state in both class and function components.
In React, state updates are inherently asynchronous processes. When you call the setState
function, React schedules an update rather than immediately applying the changes. This allows React to batch multiple state updates together, minimizing unnecessary re-renders and improving overall efficiency.
When you invoke setState
, React maintains a queue of pending updates. Then, it batches multiple setState
calls that occur within the same synchronous block of code. This batching process is crucial for preventing unnecessary re-renders and optimizing performance.
By batching state updates, React ensures that it doesn’t perform unnecessary intermediate renders. It calculates the minimal set of changes needed to update the component’s state and applies them in a single, efficient rendering cycle.
To update state in React components, we’ll use either the this.setState
function or the updater function returned by the React.useState()
Hook in class and function components, respectively.
State updates in React are asynchronous; when an update is requested, there is no guarantee that the updates will be made immediately. The updater functions enqueue changes to the component state, but React may delay the changes, updating several components in a single pass.
For example, consider the code below:
const handleClick = () => { setName("Amaka") setAge(20) setAddress("No 3 Rodeo drive") }
In this snippet, there are three different calls to update and re-render the component. Calling the updater functions one after another and re-rendering both parent and child components after each call would be inefficient in most cases. For this reason, React batches state updates.
No matter how many setState()
calls are in the handleClick
event handler, they will produce only a single re-render at the end of the event, which is crucial for maintaining good performance in large applications. The order of requests for updates is always respected; React will always treat the first update requests first.
We’ve established that delaying the reconciliation of update requests in order to batch them is beneficial. Next, we’ll explore managing situations where you must wait for these updates to complete before utilizing the new values.
setState()
callbackThe second parameter to setState()
is an optional callback function. This argument will be executed once setState()
is completed and the component is re-rendered. The callback function is guaranteed to run after the state update has been applied:
handleSearch = (e) => { this.setState({ searchTerm: e.target.value },() => { // Do an API call with this.state.searchTerm }); }
componentDidUpdate
The componentDidUpdate
function is invoked immediately after a state update occurs. To avoid an infinite loop, you should always use a conditional statement to be sure that the previous state and the current state are not the same:
componentDidUpdate(prevProps, prevState) { if (prevState.count !== this.state.count) { // Do something here } }
useEffect()
HookYou can perform side effects in the useEffect
Hook when the state is updated. The state variable could be added as a dependency in this Hook, making it run when the state value changes. You can make the useEffect
Hook listen to the state changes:
import React,{useState, useEffect} from 'react'; const App = () => { const [count, setCount] = useState(1); useEffect(() => { if (count > 5) { console.log('Count is more that 5'); } else { console.log('Count is less that 5'); } }, [count]); const handleClick = () => { setCount(count + 1); }; return ( <div> <p>{count}</p> <button onClick={handleClick}> add </button> </div> ); }; export default App;
The callback function in the useEffect
Hook runs only when there is a change in any of the state variables listed in its dependency array.
useState()
callbackThe useState
Hook in React does not have a built-in callback like setState
does. However, you can achieve similar functionality using the useEffect
Hook, which allows you to perform side effects after the component has rendered.
Here’s an example of how you might use useState
with a callback-like behavior:
import React, { useState, useEffect } from 'react'; function MyComponent() { const [searchTerm, setSearchTerm] = useState(''); const handleSearch = (e) => { setSearchTerm(e.target.value); }; useEffect(() => { // This code will run after every render, // similar to a callback after state is updated console.log('State updated:', searchTerm); // You can perform other actions here, such as an API call // Example: makeApiCall(searchTerm); }, [searchTerm]); // The effect depends on the searchTerm state return ( <div> <input type="text" value={searchTerm} onChange={handleSearch} /> {/* Other JSX elements */} </div> ); } export default MyComponent;
In this example, the useEffect
Hook runs after each render of the component, but it’s only dependent on changes to the searchTerm
state (specified in the dependency array). This mimics the behavior of a callback that runs after searchTerm
is updated.
In React, every state update causes the component being updated to re-render. Because re-rendering is expensive, making state updates synchronously can cause serious performance issues, for example, increasing load times or causing your application to crash. By batching state updates, React avoids unnecessary re-renders, boosting performance overall.
I hope you enjoyed this article!
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowThe useReducer React Hook is a good alternative to tools like Redux, Recoil, or MobX.
Node.js v22.5.0 introduced a native SQLite module, which is is similar to what other JavaScript runtimes like Deno and Bun already have.
Understanding and supporting pinch, text, and browser zoom significantly enhances the user experience. Let’s explore a few ways to do so.
Playwright is a popular framework for automating and testing web applications across multiple browsers in JavaScript, Python, Java, and C#. […]
2 Replies to "Why React doesn’t update state immediately"
Hi, I’m using classes, Parent and Child and trying to set a new state value after the DOM has loaded. I can see that you mention state does not update after setState() and kind of show how to get around that, but I feel your not providing a clear enough picture for us React nubes on how to force a state change to be recognized. I’ve been at for a few days and am a bit stumped. I just keep seeing the old value. Updates are firing and passing the old value back to the child class.
Hi, okay state update will not happen immediately as it is asynchronous. Now what we can do to update state immediately.