Lesson 39 of 80
React groups multiple state updates into one re-render instead of one per call — and since React 18, it does that everywhere, not just inside event handlers.
12 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
This topic is about judgment and practical tradeoffs more than a single right answer — a mock interview is the better way to test yourself on it.
Be interviewed on this →Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
Call setState three times in a row and you might expect three re-renders. React doesn't work that way: it batches state updates that happen close together into a single re-render. All three updates get applied, then the component renders once with the final result.
Before React 18, that batching only happened inside React's own event handlers (onClick, onChange, and so on). Anything outside that — a .then() on a fetch promise, a raw setTimeout, a native addEventListener callback — ran unbatched, so each setState call there triggered its own separate re-render. React 18 made batching automatic everywhere, closing that gap.
function handleClick() {
setCount((c) => c + 1);
setFlag((f) => !f);
// React 18+: one re-render, both updates applied together.
// Pre-18: also one re-render — this is inside a React event handler.
}
function handleFetchClick() {
fetch("/api/thing").then(() => {
setCount((c) => c + 1);
setFlag((f) => !f);
// React 18+: still one re-render.
// Pre-18: TWO re-renders — this runs after the promise resolves,
// outside any React event handler, so each call used to fire alone.
});
}
The click handler behaved the same in both versions. The fetch callback is where the change actually shows up: pre-18, React had no way to know two setState calls inside a .then() were related, so it rendered after each one. React 18's automatic batching applies the same "collect updates, render once" rule no matter where the updates come from.
setCount(c => c + 1) call still sees the latest pending state via the updater-function form, batched or not — batching doesn't change what value you end up with, only how many times the component renders on the way there.async/await.flushSync is an escape hatch, not a default. Reaching for it out of habit defeats the exact optimization automatic batching was built to give you for free.setState call ran a fresh render immediately.This connects directly to the earlier lesson on what actually causes a component to re-render: a state update is still the trigger. Batching doesn't change what counts as a trigger — it changes how many of those triggers collapse into a single actual render pass.