Join the discussion
Comments
Loading comments...
Cookie preferences
We use essential cookies for sign-in and preferences. With your permission, we also use basic analytics to improve lessons.
Join the discussion
Loading comments...
We use essential cookies for sign-in and preferences. With your permission, we also use basic analytics to improve lessons.
ReactがパフォーマンスのためにsetState呼び出しを1回の再レンダリングにまとめる方法。
状態バッチ処理とは、Reactが同じ同期ブロックからの複数のsetState呼び出しを収集し、1回の再レンダリングパスですべてを処理することです。個々のsetStateの後に毎回再レンダリングする代わりに、Reactは現在の「バッチ」の終わりまで待ち、すべての更新を一度に適用し、ちょうど1回レンダリングします。React 18は自動バッチ処理を非同期コードとネイティブイベントリスナーにも拡張しました。
クリックハンドラ内でsetA(1)とsetB(2)を呼び出すとき、ReactはsetAの後すぐに再レンダリングしません。両方の更新をキューに入れ、1回の再レンダリングをスケジュールします。コンポーネントは両方の新しい値が適用された状態で1回レンダリングされます。
各setStateCallはHookの循環キューにUpdateオブジェクトを追加します。Reactは保留中の作業があるとしてFiberをマークし、キューをフラッシュするためのマイクロタスク(またはコールバック)をスケジュールします — ただし現在の同期コードが終了した後のみです。
React 17では、バッチ処理はReactイベントハンドラ内でのみ行われました。setTimeout、Promise、またはfetchコールバック内のsetState呼び出しは個別に再レンダリングをトリガーしました。React 18はすべての更新を発生元に関わらず自動的にバッチします。
(state変更後のレイアウト測定など)同期的なDOM更新が必要な場合は、ReactDOM.flushSync(() => setState(...))を呼び出してください。これによりReactは更新を即座かつ同期的にフラッシュし、バッチ処理をバイパスします。
複数のsetStateCallが前の値に依存する場合、関数型形式を使用してください:setState(prev => prev + 1)。ReactはキューOrder通りにこれらの更新を適用するため、各更新はレンダリング時の値ではなく前の結果を参照します。
React 18の機能:setTimeout、Promise、ネイティブイベントリスナー内であっても、すべてのsetState呼び出しがバッチされます。不要な再レンダリングを削減します。
保留中のsetStateCallを格納する各Hookオブジェクト上の循環リンクリスト。各バッチの終わりにフラッシュされます — すべての更新が順番に適用されます。
保留中のstate更新を同期的かつ即座にフラッシュするようReactを強制します。バッチ処理をバイパスします。state変更後にDOM測定が必要な場合のみ使用してください。
setState(prev => newValue)。更新がバッチされているか非同期でキューされている場合でも、最新のstate値を受け取ることが保証されます。
バッチ処理によりレンダリング回数が削減されます。バッチなし:N回のsetStateCall = N回のレンダリング。バッチあり:N回のsetStateCall = 1回のレンダリング。
1// React 18: ALL of these batch into 1 render23// Inside event handler (always batched)4function handleClick() {5 setCount(c => c + 1); // queued6 setFlag(true); // queued7 setName('Alice'); // queued8 // 1 render happens here9}1011// Inside setTimeout — NOW also batched in React 1812setTimeout(() => {13 setCount(c => c + 1); // queued14 setFlag(true); // queued15 // 1 render (React 17 would have triggered 2!)16}, 0);1718// Opt out of batching when needed:19import { flushSync } from 'react-dom';20flushSync(() => setCount(1)); // renders immediately21flushSync(() => setFlag(true)); // renders again22// 2 renders total
バッチ処理はReactの最も重要なパフォーマンス最適化の1つです。バッチ処理なしでは、3つのstate更新をトリガーする単一のユーザー操作が3回の完全なレンダリング + リコンシリエーション + コミットサイクルを引き起こします。バッチ処理ありでは常に1回だけです。React 18の自動バッチ処理は非同期コードに特に価値があります — サーバーレスポンス、WebSocketメッセージ、タイマーはもはやレンダラーを酷使しません。
A login form validates email, password, and shows/hides error messages. The submit handler calls `setEmailError(...)`, `setPasswordError(...)`, `setIsSubmitting(false)` — three state updates that should be a single render.
In React 17, calling setState inside setTimeout or fetch().then() didn't batch — each call triggered a separate render. Users would see a flash: first the email error appears, then the password error, then the loading spinner disappears. Three renders for one validation check.
React 18's automatic batching ensures all three setState calls result in ONE render, regardless of whether they're inside an event handler, setTimeout, fetch callback, or promise. The user sees all errors and the stopped spinner simultaneously — no flash.
Takeaway: React 18's automatic batching is a free performance upgrade for existing code. If you migrated from React 17 and had flushSync workarounds for batching, you can now remove them — batching works everywhere automatically.
You need to update a component's state and immediately measure the resulting DOM height to pass to a third-party animation library. With batching, the DOM hasn't updated yet when you try to measure.
Automatic batching defers the DOM update. After calling `setExpanded(true)`, the DOM still shows the collapsed state. Measuring `element.offsetHeight` returns the old height. The animation library receives the wrong value.
Wrap the state update in `flushSync()`: `flushSync(() => setExpanded(true))`. This forces React to immediately commit the DOM update synchronously. The next line can safely measure `element.offsetHeight` and get the expanded height. Use sparingly — it opts out of batching's performance benefit.
Takeaway: flushSync is the escape hatch when you need synchronous DOM access after a state update. Common use cases: third-party animation libraries, scroll position restoration, and focus management. Use it only when batching conflicts with imperative DOM operations.