Skip to main content

setState in React 18

1. The main change - asynchrony and "smart" priority control

Before React 18, every setState() update was synchronous within a single render. React would immediately trigger a re-render as soon as you called setState() (with possible batching inside an event).

Now, in React 18+ (Concurrent Mode):

setState() can run asynchronously and concurrently - React can defer, pause, merge, or cancel a render if other (higher-priority) updates are happening at the same time.

More simply:

React now decides:

  • "Is this an urgent update? Then do it right away."
  • "Is this not urgent (for example, filtering a list)? Then defer it while the user is typing."

2. Automatic Batching now works everywhere

Previously, batching (merging several setState() calls into one render) only worked inside React events (onClick, onChange, and so on). Now it works in any context: setTimeout, Promise, async/await, fetch, and even inside custom hooks.

javascript
setTimeout(() => { setCount(c => c + 1); setFlag(f => !f); }, 1000); // React 18: one combined render // React 17: two separate renders

In other words: React now automatically groups all state updates within a single "iteration" of the event loop.


3. setState() can now be "deferred"

React 18 added the startTransition() API and the useTransition() hook, which let you explicitly mark updates as "low priority".

javascript
const [isPending, startTransition] = useTransition(); function handleChange(e) { const value = e.target.value; startTransition(() => { setFilter(value); // React will run this later, so it does not block input }); }

Here:

  • the filter update (low priority) runs "in the background";
  • the user's input (high priority) is handled instantly.

Previously, React could not split update priorities; now it can.


4. React can interrupt or cancel an in-progress render

If new state arrives while a render is in progress, React can:

  • cancel the render that already started (if it has not finished yet);
  • start a new one with more up-to-date data.

This became possible thanks to Concurrent Rendering. In old React 17, the rendering process was blocking - React had to "run to completion".


5. Ordering and predictability

The "update queue" mechanism (the setState queue) has become more flexible:

ScenarioReact 17React 18
Several setState calls in a rowRan synchronously in a single render (inside an event)Still merged, but order is guaranteed even with async calls
Updates in promisesEach setState triggered a separate renderAll of them merge into a single combined render
Updates inside startTransitionDid not existUpdates run later, with low priority

6. A comparison example

React 17 (outdated behavior)

javascript
setTimeout(() => { setCount(c => c + 1); setFlag(f => !f); }, 1000); // Runs 2 renders (each setState in turn)

React 18 (new behavior)

javascript
setTimeout(() => { setCount(c => c + 1); setFlag(f => !f); }, 1000); // Runs 1 render (automatic batching)

7. Forcing an update (flushSync)

If you need to update the DOM right away, bypassing batching, you can use flushSync():

javascript
import { flushSync } from 'react-dom'; flushSync(() => { setCount(c => c + 1); }); // React re-renders the component immediately

This is useful when you need the update to appear before the next frame (for example, for precise layout measurements).


8. Behavior in Strict Mode (in dev mode)

In React 18, Strict Mode mounts and unmounts components twice, to surface side effects related to asynchronous setState.

If you see setState being called twice, that is not a bug - it is a mechanism for checking the correctness of side effects.


Summary

In React 18, setState() became:

  • asynchronous and concurrent - React can queue updates and manage their priority;
  • automatically batched in any context;
  • interruptible - an in-progress render can be canceled if new state arrives;
  • more performant and smoother for UX.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.