以前に述べたように、React でリストをレンダリングする際には、各データに key 値を付与し、確定的な識別子を与える必要があります。そして、識別子を付与する方法についても詳しく説明しましたが、なぜこれを行う必要があるのでしょうか?
デフォルトの条件下では、DOM ノードの子要素を再帰的に処理する際、React は 2 つの子要素のリストを同時に走査します。差異が生じると、mutation が生成されます。
子要素リストの末尾に要素を追加する場合、更新のオーバーヘッドは比較的小さくなります。例えば:
<ul>
<li>first</li>
<li>second</li>
</ul>
<ul>
<li>first</li>
<li>second</li>
<li>third</li>
</ul>
React は最初に 2 つの <li>first</li>
に対応するツリーをマッチングし、次に 2 番目の要素 <li>second</li>
に対応するツリーをマッチングし、最後に 3 番目の要素の <li>third</li>
ツリーを挿入します。
もし単純に新しい要素を表頭に挿入するだけであれば、更新のオーバーヘッドは比較的大きくなります。例えば:
<ul>
<li>Duke</li>
<li>Villanova</li>
</ul>
<ul>
<li>Connecticut</li>
<li>Duke</li>
<li>Villanova</li>
</ul>
React は <li>Duke</li>
と <li>Villanova</li>
を保持すべきだと認識せず、すべての子要素を再構築します。この状況はパフォーマンスの問題を引き起こします。
key
上記の問題を解決するために、React は key
属性をサポートしています。子要素が key を持つ場合、React は key を使用して元のツリーの子要素と最新のツリーの子要素をマッチングします。以下の例では、key
を追加することで以前の非効率な変換が効率的になります:
<ul>
<li key="1">Duke</li>
<li key="2">Villanova</li>
</ul>
<ul>
<li key="0">Connecticut</li>
<li key="1">Duke</li>
<li key="2">Villanova</li>
</ul>
これで React は、'0'
key を持つ要素が新しい要素であり、'1'
および'2'
key を持つ要素は単に移動しただけであることを理解します。
実際のシナリオでは、key を生成することは難しくありません。表示する要素にはすでに一意の ID がある場合が多く、そのため key はデータから直接抽出できます:
<li key={item.id}>{item.name}</li>
上記の状況が成立しない場合は、モデルに ID フィールドを追加するか、一部の内容をハッシュ値として使用して key を生成できます。この key は全体で一意である必要はありませんが、リスト内では一意である必要があります。
最後に、要素の配列内のインデックスを key として使用することもできます。この戦略は、要素が再配置されない場合に適していますが、順序が変更されると diff が遅くなります。
インデックスに基づくコンポーネントを再配置する際、コンポーネントの状態にいくつかの問題が発生する可能性があります。コンポーネントインスタンスは key に基づいて更新および再利用されるかどうかが決まるため、key がインデックスである場合、順序を変更すると現在の key が変更され、非制御コンポーネントの状態(例えば入力ボックス)が互いに干渉し、予期しない変化を引き起こす可能性があります。