Suggest an editImprove this articleRefine the answer for “setState in React 18”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)In React 18, **`setState()`** became asynchronous and concurrent: React can queue updates, manage their priority, and batch them in any context (not just inside React events). **Key point:** automatic batching now works everywhere - in `setTimeout`, `Promise`, `async/await`, and even custom hooks, so several `setState` calls result in a single render.Shown above the full answer for quick recall.Answer (EN)Image## 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**: | Scenario | React 17 | React 18 | |---|---|---| | Several `setState` calls in a row | Ran synchronously in a single render (inside an event) | Still merged, but order is guaranteed even with async calls | | Updates in promises | Each `setState` triggered a separate render | All of them merge into a single combined render | | Updates inside `startTransition` | Did not exist | Updates 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**.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.