DEV Community

Cover image for Why React doesn’t update state immediately
Matt Angelosanto for LogRocket

Posted on • Updated on • Originally published at blog.logrocket.com

Why React doesn’t update state immediately

Written by Chiamaka Umeh✏️

Despite React’s popularity, one of its biggest drawbacks is its components re-rendering excessively. 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 altered later on 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. Let's get started!

How React performs state updates

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")
}
Enter fullscreen mode Exit fullscreen mode

In the code snippet above, 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.

Now that we’ve established that delaying reconciliation of updates requests in order to batch them is beneficial, there are also times when you need to wait on the updates to do something with the updated values. In the next section, we’ll see how to do that.

Carrying out operations with class components

setState() callback

The 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
  });
}
Enter fullscreen mode Exit fullscreen mode

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
  }
}
Enter fullscreen mode Exit fullscreen mode

Carrying out operations with function components

useEffect() Hook

You 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;
Enter fullscreen mode Exit fullscreen mode

The callback function in the useEffect Hook runs only when the state variable provided as a dependency changes.

Conclusion

In React, every state update causes the component being updated to re-render. Because re-rendering is an expensive operation, 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!


Full visibility into production React apps

Debugging React applications can be difficult, especially when users experience issues that are hard to reproduce. If you’re interested in monitoring and tracking Redux state, automatically surfacing JavaScript errors, and tracking slow network requests and component load time, try LogRocket.

LogRocket signup

LogRocket is like a DVR for web and mobile apps, recording literally everything that happens on your React app. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app's performance, reporting with metrics like client CPU load, client memory usage, and more.

The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores.

Modernize how you debug your React apps — start monitoring for free.

Discussion (0)