⚛️ React Boundary
内部構造 — 深掘り

Reconciliationの内部

なぜReactのdiffはO(n)で済むのか。keyがアルゴリズムにどう影響するか。疑似コードで内部実装を読み解きます。

Reconciliationとは何か

stateが変化するたびにReactは新しいVirtual DOMツリーを生成します。しかし、新しいツリーをそのままDOMに反映するのは非効率です。 そこでReactは前回のツリーと新しいツリーを比較(diff)し、変化した最小限の箇所だけをDOMに反映します。 このプロセスを Reconciliation(リコンシリエーション) と呼びます。

Reconciliationの流れ(概念図)
前回の
Virtual DOM
→ diff →
新しい
Virtual DOM
↓ 差分だけ
実際のDOMを最小限更新

一般的なツリーのdiffアルゴリズムはO(n³)かかります。しかしReactのdiffはO(n)です。 どうやってそれを実現しているのでしょうか?

Reactが採用する2つの仮定

  1. 異なる型の要素は、異なるツリーを生成する(型が変わったら再構築)
  2. keyによって、リスト内の要素を安定的に識別できる

この2つの仮定によって、ReactはO(n³)の問題をO(n)に落とし込んでいます。

O(n³) → O(n):なぜ可能なのか

2つの任意のツリー間の最小編集距離を求める最良の既存アルゴリズムはO(n³)の複雑度を持ちます。 1000要素のReactアプリでは10億回の計算が必要になり、実用的ではありません。

Reactは2つのヒューリスティック仮定を用いてこれをO(n)に落とし込みます。

仮定1: 異なる型 → 完全な再構築

ReactはコンポーネントA(例: <div>)が <span>に変わった場合、 サブツリー全体を破棄して再構築します。diffを計算しません。

// 旧ツリー
<div><Counter /></div>
// 新ツリー(型が変わった)
<span><Counter /></span>
// → div以下のツリーを破棄、spanで再構築
// Counter のstateもリセットされる

仮定2: keyによる同一性識別

同じ型の子要素リストに対し、Reactは上から順番に比較します(インデックスベース)。 しかしkeyがある場合、 Reactはkeyで要素を識別し、移動・追加・削除を正確に追跡します。

// 先頭に要素を追加する場合
// keyなし: 全要素が変化したとみなされる
// O(n)の不要な更新が発生
// keyあり: Reactは追加を正確に識別
// 新要素だけを挿入する (O(1))

疑似コードで読むReconciliation

実際のReactソースコードを簡略化した疑似コードでReconciliationのコアロジックを理解します。

function reconcileChildren(current, workInProgress, newChildren) {
  // keyがある場合は Map で O(1) ルックアップ
  const existingChildren = mapRemainingChildren(current);

  for (let i = 0; i < newChildren.length; i++) {
    const newChild = newChildren[i];
    const key = newChild.key ?? i; // keyなしはindexを使用

    const existing = existingChildren.get(key);

    if (existing && existing.type === newChild.type) {
      // 同じ型 → 再利用してpropsだけ更新
      updateFiber(existing, newChild.props);
      existingChildren.delete(key); // 使用済みマーク
    } else {
      // 型が違うか新規 → 新しいFiberを作成
      createFiber(newChild);
    }
  }

  // 残った既存要素は削除
  existingChildren.forEach(child => deleteChild(child));
}

この処理はMapを使っているため、各要素の検索がO(1)です。 n個の要素を処理するので全体でO(n)になります。

FiberとReconcilerの分離

React 16以降、ReconciliationはFiberアーキテクチャの上で動きます。 Fiberとは何か、なぜ中断可能なのかはFiberとは何か?で詳しく解説します。

重要な分離: ReactのコアはReconciler(何をするか決める)とRenderer(実際にDOMを操作する)に分かれています。 これがReact Native・React Three Fiberなどが存在できる理由です。

関連記事