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 18がタイムスライシングと優先度レーンを使ってUIの応答性を維持する方法。
並行レンダリングはReact 18の機能で、複数バージョンのUIを同時に準備し、長時間実行されるレンダリングを中断してより緊急の更新を処理できます。レンダリングがメインスレッドをブロックする単一の同期操作である代わりに、Reactはレンダリング作業をツリーの途中で一時停止し、ブラウザに制御を譲り、緊急の更新を処理し、その後一時停止した作業を再開または破棄できます。
ReactのFiberリコンサイラーは各コンポーネントを小さな作業単位(Fiber)として表現します。レンダーフェーズは一度に1つのFiberを処理し、各ユニットの後により高い優先度の作業が到着したかどうかを確認します。その場合、一時停止して後で戻ることができます。
Reactのスケジューラーはレンダリング作業を小さなチャンクに分割します。各チャンクの後、ブラウザに制御を戻します — 入力イベント、アニメーション、描画が発生できるようにします。これにより長いレンダリング中にUIがフリーズするのを防ぎます。
React 18は「レーン」モデルを使用して更新を優先度で分類します:SyncLane(ユーザー入力、即時必須)、InputContinuousLane(継続的なジェスチャー)、DefaultLane(通常の状態更新)、TransitionLane(非緊急のUIトランジション)。
高優先度の更新が低優先度のレンダリング中に到着した場合、Reactは低優先度の作業を一時停止し、高優先度の更新を完全に処理(コミットを含む)し、その後低優先度の作業を再開または再開始します。
レンダーフェーズは純粋(副作用なし)であるため、Reactは部分的に完了したレンダリングを安全に破棄して、アプリケーションの状態を破損させることなく再開始できます。副作用はコミットフェーズでのみ発生し、コミットフェーズは中断されることはありません。
レンダリング作業を約5msのチャンクに分割し、チャンク間でブラウザに制御を譲ります。高コストなレンダリング中もUIの応答性を維持します。
更新をSyncLane(最も緊急)からOffscreenLane(最も緊急でない)まで分類するビットマスクシステム。スケジューリングの順序を決定します。
Reactの内部タスクスケジューラー。レンダリングタスクの優先度キューを管理し、優先度と期限に基づいて次に何を処理するかを決定します。
状態更新を非緊急トランジションとしてマークするAPI。より緊急の更新のためにこの作業を中断できることをReactに伝えます。
startTransitionとトランジションが進行中かどうかを示すisPendingブール値を提供するHook。
1import { useState, useTransition } from 'react';23function SearchPage() {4 const [query, setQuery] = useState('');5 const [results, setResults] = useState([]);6 const [isPending, startTransition] = useTransition();78 function handleInput(e) {9 // Urgent: update the input immediately10 setQuery(e.target.value);1112 // Non-urgent: filtering 10,000 results can wait13 startTransition(() => {14 setResults(filterItems(e.target.value));15 });16 }1718 return (19 <>20 <input value={query} onChange={handleInput} />21 {isPending && <Spinner />}22 <ResultsList results={results} />23 </>24 );25}26// Typing feels instant (urgent update)27// Results update when React has time (transition)
React 18以前は、遅いレンダリングをトリガーする状態更新はレンダリングが完了するまでUI全体をフリーズさせました。並行レンダリングにより、入力への入力、スクロール、ボタンのクリックは、Reactがバックグラウンドで複雑な更新を計算している間も応答性を維持します。これはSuspense、ストリーミングSSR、React Server Componentsの基盤です。
A product catalog with 50,000 items needs instant search. Using debounce (300ms delay) feels sluggish. Without debounce, every keystroke triggers a heavy filter that blocks the main thread and makes typing lag.
Traditional approach: debounce → wait 300ms → filter → re-render. The user types 'laptop' and sees nothing for 300ms after each keystroke. Or without debounce: type 'l' → 50ms filter blocks thread → 'a' keystroke is delayed → laggy typing experience.
Use `startTransition` to mark the filter as non-urgent. React processes the input update immediately (typing stays responsive), then renders the filtered results when the main thread is free. If the user types another character before the filter completes, React ABANDONS the in-progress render and starts the new filter — no wasted work.
Takeaway: Concurrent rendering replaces debouncing for CPU-heavy UI updates. It provides a better UX (no artificial delay) with better performance (abandoned renders waste no DOM work). Use useTransition for filtering, sorting, and computing derived state.
Clicking a navigation link to a data-heavy dashboard page freezes the UI for 500ms while the new page renders 200+ chart components. The old page disappears, a blank screen shows, then the new page appears — jarring.
Without concurrent rendering, React must complete the entire render synchronously. Clicking 'Dashboard' unmounts the current page and starts rendering 200 charts. The main thread is blocked for 500ms — no animations, no response to user input, just a white flash.
Wrap the navigation in `startTransition(() => navigate('/dashboard'))`. React keeps the old page visible and interactive while rendering the dashboard in the background. If the user clicks 'Back' during this time, React abandons the dashboard render. The transition completes smoothly with no blank screen.
Takeaway: Concurrent rendering enables 'optimistic UI' for navigation — the old page stays visible until the new one is fully ready. This eliminates white-flash navigation transitions and lets users cancel slow navigations by clicking elsewhere. Next.js App Router uses this pattern by default.