初めに
Reactで開発をしていると、ほぼ確実に目にするのが次の警告です。
Warning: Each child in a list should have a unique "key" prop
初めて見ると「動いてはいるけど、なぜ怒られているんだろう?」と戸惑う人も多いと思います。
この警告は、リストレンダリング(.map() など)を行う際に 子要素に一意の key が割り当てられていない 場合に発生します。
このエラーが出る背景と正しい対処方法、さらに実務で遭遇しやすいパターンまで整理して解説していきます。
よくある症状
- コンソールに上記の警告が表示される(ときには “Check the render method of XXX” というヒント付き)。
- 実際の原因箇所が特定しづらく、親コンポーネント側にまとめて表示されがち。
- 入力フォームのフォーカスが勝手に外れる、リストの行の状態が入れ替わるなど、副作用的な不具合につながることもある。
なぜ起きる?
- React はリスト描画時に 兄弟要素の配列を比較し、
keyを手掛かりに「同じ要素かどうか」を判断する。 - そのため
.map()が返す最上位の要素にkeyを付ける必要がある。子要素や孫要素に付けても意味がない。 -
短縮フラグメント
<>...</>にはkeyを指定できないため、必要な場合は<React.Fragment key="...">...</React.Fragment>を使う。
最小再現(悪い例 → 良い例)
悪い例:最上位に key がない
{items.map((item) => {
return (
<div>
<Cell value={item.name} />
</div>
);
})}
この場合、.map() が返す最上位要素(ここでは <div>)に key が付与されていないため警告が出ます。
良い例A:最上位に key を付ける(安定した ID を推奨)
{items.map((item) => (
<div key={item.id}>
<Cell value={item.name} />
</div>
))}
良い例B:フラグメントに key を付ける
{items.map((item) => (
<React.Fragment key={item.id}>
<Cell value={item.name} />
</React.Fragment>
))}
※短縮記法 <>...</> では key を付けられないため注意。
良い例C:ヘルパーはコンポーネント化する
{items.map((item) => (
<Row key={item.id} item={item} />
))}
リストの行が複雑になる場合は、専用コンポーネント化した方が可読性・再利用性も高まります。
補足:
{ ... } を使ったアロー関数は return の書き忘れが多発します。
シンプルに書ける 暗黙的 return(=> ( ... )) を使うと安全です。
ありがち落とし穴
-
indexをkeyに使う
並べ替え・挿入・削除が発生するリストでは不適切。要素のstateがズレたり、不要な再マウントが頻発する原因になる。 -
ランダム値やタイムスタンプを
keyに使う
毎回変わる=React にとって「毎回別の要素」扱い。入力中の値やフォーカスがリセットされる。 -
短縮フラグメント
<>...</>
keyを付けられないため、必要な場合はReact.Fragmentを明示的に使う。 -
複数配列を結合して描画する
例:[...A.map(), ...B.map()]
同じ親配列に並ぶためkeyが衝突しやすい。
→A:${id}やB:${id}のようにプレフィックスを付けて区別する。 -
子コンポーネント内部の
.map()を見落とす
親でkeyを付けても、子の配列レンダーにkeyが無ければ同様に警告が出る。
デバッグ手順(汎用)
-
map/return [/<>を全文検索
→ 各配列レンダーの 最上位要素にkeyがあるかを確認する。 -
短縮フラグメントを見直す
<>...</>を使っている場合はReact.Fragmentに置き換え、必要ならkeyを付与する。 -
ヘルパー関数の返り値を確認
関数が直接 JSX を返しているなら、関数コンポーネント化して<Helper ... key={...} />の形にする。 -
配列結合を点検
[..., ...]で複数の配列を展開している場合、プレフィックス付きのキー(例:A:${id}/B:${id})で一意化する。 -
ESLint を活用
ルール"react/jsx-key": "error"を有効化して、自動的に検出できるようにする。 -
React DevTools で確認
Components タブから要素を選び、keyが付与されているか・値が安定しているかを目視チェックする。
ベストプラクティス
-
keyは 安定・一意・不変な値を使う
(例:DBのid、永続的なコードなど) -
.map()が返す最上位要素に必ずkeyを付与する
子要素や孫要素に付けても効果はない - Fragment を使うときは
<React.Fragment key="...">を利用する - 複数系列の配列を同じ親に並べるときは プレフィックスを付与して衝突回避
-
indexは最終手段
(並び順が固定されていて、配列が変化しない場合にのみ利用可)
まとめ
-
この警告の本質は 「兄弟要素の配列レンダーには、最上位に安定した
keyが必須」 ということ。 -
対処に迷ったら:
- 最上位に
keyを付ける - Fragment は
React.Fragmentを使う - 複雑になったらコンポーネント化する
- 最上位に
-
仕上げに ESLint と React DevTools を組み合わせれば、再発を防ぎつつ安心して開発できる。