Automatic Batching Divergence Between React 17 Development and React 18 Production
0 reputation · 09 May 2025, 20:05 UTC
0 reputation · 09 May 2025, 20:05 UTC
In React 17 development, state updates inside event handlers are automatically batched while updates in asynchronous callbacks remain unbatched.
React 18 production enables automatic batching for all updates inside event handlers, which can combine multiple setState calls into a single render and affect side‑effect ordering.
35275 reputation · 10 May 2025, 06:59 UTC
ReactDOM.unstable_batchedUpdates in production? No. In React 18 with createRoot, automatic batching for promises, timeouts, and native handlers is intended behavior. There is no supported global off switch; use flushSync only when a specific update must be flushed synchronously.createRoot API, not to opting into concurrent features like startTransition or useDeferredValue.createRoot everywhere). Assert committed DOM and effect callbacks (from useEffect/useLayoutEffect) rather than raw render counts, and account for React 18 StrictMode double‑rendering in development.createRoot batches all updates by default in both development and production.ReactDOM.render in React 18 intentionally preserves the old React 17 batching behavior.createRoot (not ReactDOM.render).ReactDOM.renderReactDOM.rendercreateRoot without StrictModecreateRoot with StrictMode (development only)setState calls inside a Promise or setTimeout and record:
useEffect counter)act/waitFor (or equivalent) with the React act environment enabled so async updates are flushed deterministically before assertions.Does the React 18 production app call createRoot or legacy ReactDOM.render, and does the development build wrap the tree in <React.StrictMode>? If legacy render is used, the observed divergence is not caused by React 18's automatic batching change.
Use comments to ask for clarification. Post a solution as an answer.
35,275 reputation · 09 May 2025, 23:19 UTC
It is important to clarify that React 18's automatic batching is confined to state updates occurring directly within React event handlers. Updates scheduled via non-event asynchronous APIs, such as setTimeout or Promises, remain unbatched across both React 17 (legacy) and React 18 (createRoot).
For consistent side-effect ordering and render counts between environments, developers must wrap state mutations triggered by async callbacks inside a synchronous context, perhaps by ensuring the async callback executes immediately after the event handler completes its synchronous work.