以前にも述べたように、React ではリストをレンダリングする際に、各データにキー値を付ける必要があります。キー値は一意の識別子を与えるものであり、どのように識別子を付けるかについても詳しく説明しました。では、なぜこれを行う必要があるのでしょうか?
デフォルトの条件では、再帰的に DOM ノードの子要素を処理する際、React は 2 つの子要素のリストを同時に走査します。差異が生じた場合、変更を生成します。
要素リストの末尾に要素を追加する場合、更新コストは比較的小さくなります。例えば:
<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>
を保持すべきであることに気づかず、各子要素を再構築します。この場合、パフォーマンスの問題が発生します。
キー
上記の問題を解決するために、React はkey
属性をサポートしています。子要素にキーがある場合、React はキーを使用して元のツリー上の子要素と最新のツリー上の子要素をマッチさせます。以下の例では、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'
キーを持つ要素だけが新しい要素であり、'1'
および'2'
キーを持つ要素は移動したことがわかります。
実際のシナリオでは、キーを生成することは困難ではありません。表示する要素にはすでに一意の ID があるかもしれないため、キーは直接データから抽出することができます:
<li key={item.id}>{item.name}</li>
これが成立しない場合は、モデルに ID フィールドを追加するか、一部のコンテンツをハッシュ値として使用してキーを生成することができます。このキーはグローバルに一意である必要はありませんが、リスト内では一意である必要があります。
最後に、配列内の要素のインデックスをキーとして使用することもできます。この戦略は要素の並べ替えが行われない場合に適しており、順序が変更されると diff が遅くなります。
インデックスベースのコンポーネントを再ソートする場合、コンポーネントの状態に問題が発生する可能性があります。コンポーネントインスタンスは、そのキーに基づいて更新と再利用が行われるため、キーがインデックスである場合、順序を変更すると現在のキーが変更され、予期しない変更が発生する可能性があります(例:入力フィールドなどの非制御コンポーネント)。