【React】keyにindexを指定してはいけない理由
keyにindexを指定するとLinterに怒られる
Linterにbiomeを使っているのですが、map
などのループ処理で要素を作る時、後でちゃんとなおすからと、ついつい手っ取り早くkey
にindex
を指定してしまうことがあります。
今回、なぜそれがダメなのかちゃんと勉強してみたので、記録します。
Avoid using the index of an array as key property in an element.biomelint/suspicious/noArrayIndexKey
何が起こるか
下記のcodesandboxはkey
にindexを指定した場合に起こる不都合を再現しています。
biomeの公式Docsでこの問題のブログ記事「indexをkeyに使うのはアンチパターン」が紹介されてますが、classコンポーネントで書かれていたので、勉強ついでに自分なりに簡易版としてリメイクしてみました。
下記のcodesandboxのコード右にあるバーをドラッグすると、プレビューが見れます。
sample code
簡略化したコード
オブジェクトが複数格納された配列arrをmapして、<label>
と<input>
を生成します。
コードは省きますが、arrに要素を追加するとarrの先頭に要素が追加されます。
export const Component = () => {
const arr = [
{ name: “Bar” }
{ name: “Foo” },
];
return (
{arr.map((item, index) => {
return(
<div key={index}>
<label>item.name</label>
<input value=””>
</div>
);
)}
);
}
indexをkeyにした場合【NG】
Reactはkey
を手がかりにDOMを管理していて、変更された箇所だけを再描画します。
なので、配列に要素を追加すると下記のようになります。
追加前
React「key=0のinput
のvalue
は”sample-text”だね!OK!」
[
{ name: "Bar" }, // keyがindex=0 => <input value="sample-text" />
{ name: "Foo" }, // keyがindex=1 => <input value="" />
]
追加後
React「key=0のinput
のvalue
は”sample-text”のハズだから、そのままでヨシ!」× NG
[
{ name: "Baz" }, // keyがindex=0 => <input value="sample-text" />
{ name: "Bar" }, // keyがindex=1 => <input value="" />
{ name: "Foo" }, // keyがindex=2 => <input value="" />
]
index以外のユニークな値をkeyにした場合【OK】
ユニークな値をkey
にしておけば、Reactは間違わない。
追加前
React「key=bのinput
のvalue
は”sample-text”だね!OK!」
[
{ name: "Bar", id=”b” }, // key=b => <input value="sample-text" />
{ name: "Foo", id=”a” }, // key=a => <input value="" />
]
追加後
React「key=bのinput
のvalue
は”sample-text”のハズだから、そのままでヨシ!」○ OK
[
{ name: "Baz", id=”c” }, // key=c => <input value="" />
{ name: "Bar", id=”b” }, // key=b => <input value="sample-text" />
{ name: "Foo", id=”a” }, // key=a => <input value="" />
]
それでもindexをkeyにしたい場合
入れ替えが絶対に発生しないことが確約されていて、他にkeyに使えるものがない場合は、Linterのignoreやコメントでその旨書いておくと良さそうです。
<form className="flex flex-col gap-3 form-group">
{todoList.map((todo, index) => {
// biome-ignore lint: 入れ替えは発生しないのでindexをkeyに設定.
return <Item todo={todo} key={index} />;
})}
</form>
Discussion