【React】map()のkeyにindexの利用が推奨されない理由
Next.jsを書いているときにmap()を多用します。
mapのkeyにindexを使うのことが良くないことは知っていたのですが、理解が不十分だと感じたのでまとめます。
keyとは?
公式サイトに以下のように書かれています。
配列内の他のアイテムと区別できるようにするための一意な文字列ないし数値のこと。
key は、配列のどの要素がどのコンポーネントに対応するのかを React が判断し、後で正しく更新するために必要です。これが重要となるのは、配列の要素が移動(ソートなどによって)した場合、挿入された場合、あるいは削除された場合です。適切に key を選ぶことで、React は何が起こったか推測し、DOM ツリーに正しい更新を反映させることができます。
keyが必要である理由
公式サイトには以下の記載があります。
デスクトップ上のファイルに名前がない場合を想像してください。代わりに、最初のファイル、2 番目のファイルといったように、順番によってそれらを区別する必要があるとしましょう。そのうち番号に慣れるかもしれませんが、ファイルを削除した途端に混乱してしまいますね。2 番目のファイルが 1 番目のファイルになり、3 番目のファイルが 2 番目のファイルになり、という具合です。
フォルダ内のファイル名と JSX の key の目的は似ています。兄弟間で項目を一意に識別できるようにするのです。適切に選択された key は、配列内の位置よりも多くの情報を提供します。並べ替えによって位置が変更されたとしても、key のおかげで React はその項目が存在する限り、それを一意に識別できるのです。
keyは会社で例えるなら社員番号のことです。
誰かが退職したことによって自分の社員番号が変わってしまったら、これまで社員番号で結びついていた社員のデータを適切に追跡できなくなってしまいます。
keyがないと、、、
まず、map()を使う際にkeyを設定しないと以下のエラーが出ます。
import React from 'react';
export default function Hoge() {
const items = ['Apple', 'Banana', 'Cherry'];
return (
<>
<h1>Hoge</h1>
<ul>
{items.map((item) => (
<li>{item}</li>
))}
</ul>
</>
);
}
エラー
Missing "key" prop for element in iterator
keyを設定するようアラートが出るため、Reactを書いているとkeyの設定が当たり前になっていますが、実はkeyの存在はReactのパフォーマンスに影響及ぼしています。
パフォーマンスへの影響
keyがない場合
変更前後の要素を上から順に比較して、差分が見つけると毎回更新します。
React は単純に、両方の子要素リストのそれぞれ最初から同時に処理を行っていって、差分を見つけたところで毎回更新を発生させます。
公式サイトに記載があります。
キャプチャ①(keyがない場合)
キャプチャ①のとおり、先頭にAppleが追加された場合、Appleだけでなく、BananaとCherryも差分として検知されてしまいます。
一意な値のkeyがある場合
Reactはkeyを使って変更前後の要素の変化を比較/認識します。
キャプチャ②(keyがある場合)
そのため、先頭にid=1のAppleが追加されたとしても、BananaとCherryは変更ではなく、ただの移動として扱ってもらえ、パフォーマンスの低下を防ぐことができます。
keyにindexを使うのは良くない
それではkeyにindexを設定した場合はどうでしょうか?
上記のとおり、Reactではkeyを使って変更前後の要素を比較しているため、ここでindexを設定してしまうと、BananaやCherryも差分として扱われてしまいます。
キャプチャ③(keyがある場合(indexを設定))
keyにindexを設定することでバグを生む可能性があります。
公式サイトからサンプルに飛ぶことができるので確認してみてください。
keyに使えるプロパティ
keyに使えるデータは以下2点を満たしている必要があります。
keyは兄弟間(対象とする配列の中)で一意であること。ただし、異なる配列に対応する JSX ノードには同じキーを使用することができます。
keyは変更されないこと。さもないと key の目的が台無しになります。レンダーの最中に key を生成してはいけません。
公式サイトに記載されています。
keyにindexを使える場合
keyに使えるデータがないとき、要素の並び順が変更されない場合はindexを最終手段として使えます。
レンダーされる要素に安定した ID がない場合、最終手段として項目のインデックスを使うことができます
要素の並び順が変更される可能性がある場合、インデックスを key として使用することはお勧めしません。パフォーマンスに悪い影響を与え、コンポーネントの状態に問題を起こす可能性があります。
公式サイトに記載されています。
最後に
keyの意味やindexの利用が推奨されない理由が分かりました。
私がコードを書く中でmapを利用するタイミングが多いのは、DBから取得した値を1つずつコンポーネントに渡すときです。
DBに格納されているということは、何かしらの一意な値(例:ID)を持つはずなので、わざわざindexを使うことはなさそうです。
参考
以下を参考にさせていただきました。
Discussion