Question presented to candidate: "React 18 introduced automatic batching. What exactly changed, and how would you opt out of it?"
What a strong answer should cover:
- Before React 18, batching only applied inside React's own synthetic event handlers. Updates in
setTimeout, promises, or native listeners each triggered their own render. - React 18 made it automatic everywhere — that is the whole change, and it was one of the few genuinely observable behavioural differences in the release.
- The mechanism: updates are queued and flushed together at the end of the current work, so the component never renders with only some applied.
- Why it was safe to change: batching is semantically invisible unless you were relying on an intermediate render, which was already fragile.
- Opting out:
flushSync— forces React to process an update synchronously before continuing. Costs an extra render and the batching you gave up. - Legitimate uses of
flushSync: measuring the DOM after a state change, or integrating with a non-React system that reads the DOM immediately. unstable_batchedUpdateswas the pre-18 way to opt in manually; it still exists for compatibility but is no longer needed.- Concurrent features build on this: transitions rely on React controlling when work is flushed.
Clarifying questions expected:
- "Which React version is the codebase on?" — the answer genuinely differs.
- "Do you need the DOM updated before the next line, or just eventually?"
Code / implementation expected: Yes — updates across contexts, plus flushSync and what it costs.