Reactのレンダリングフェーズ
Reactは「レンダリング」を一度に行わない。render phaseとcommit phaseに分け、それぞれ異なる目的で実行します。
🎯 結論
Reactのレンダリングは2フェーズに分かれています:
① render phase — 「何を変えるか」を計算する(純粋な計算)
② commit phase — 「実際にDOMを変える」(副作用を伴う実行)
この分離がReactのConcurrent Modeを可能にし、UIの応答性を保つ基盤になっています。
📐 2つのフェーズの全体像
🔵 Render Phase:純粋な計算
render phaseでは、Reactはコンポーネント関数を実行して新しいVirtual DOMツリー(Fiberツリー)を構築し、 前回のツリーと比較してどこが変わったかを計算します。
重要:render phaseでDOMは変わらない
render phaseは完全に「計算だけ」です。実際のDOMには一切触れません。 だからこそ、Concurrent Modeではこのフェーズを途中で中断して、より優先度の高いタスク(ユーザー入力など)を先に処理できます。
// render phaseではこういう処理が起きている
function performUnitOfWork(fiber) {
// コンポーネント関数の実行(例: () => <div>...</div>)
const newChildren = fiber.type(fiber.props);
// 子要素のFiberを作成 & 前回との差分を記録
reconcileChildren(fiber, newChildren);
// 次に処理するFiberを返す(中断可能)
return fiber.child || fiber.sibling;
} 🟢 Commit Phase:副作用の実行
commit phaseでは、render phaseで作成した「差分リスト」を使って実際のDOMを更新します。 このフェーズは同期的・中断不可能で実行されます。
commit phaseの3段階
useEffect vs useLayoutEffect
useLayoutEffectはcommit phase内(ブラウザ描画前)に同期実行されます。
一方useEffectはcommit phase完了後、ブラウザが描画した後に非同期で実行されます。
DOM計測が必要な場合はuseLayoutEffect、通常の副作用はuseEffectを使います。
⚡ なぜ分けることが重要なのか?
render phaseとcommit phaseを分けることで、ReactはConcurrent Modeを実現できます。
// Concurrent Modeのイメージ [render phase - ユーザーリスト更新中...] → ユーザーがキーを入力!(高優先度) → 現在のrender phaseを中断 → 入力のrender phaseを先に実行・commit → 元のrender phaseを再開・commit
📌 まとめ
- ✓ render phase:コンポーネントを実行して差分を計算する(DOMは変えない)
- ✓ commit phase:実際のDOMを変える(中断不可・同期的)
- ✓ render phaseが中断可能なことがConcurrent Modeの基盤
- ✓ useLayoutEffectはcommit phase内(ブラウザ描画前)、useEffectはその後(非同期)