React で「Each child in a list should have a unique ‘key’ prop」警告を正しく理解する

React を使っていると、次のような警告を一度は目にしたことがあるでしょう。

Warning: Each child in a list should have a unique "key" prop.

このメッセージは単なる注意ではなく、仮想DOMの差分更新(reconciliation)に関わる重要な設計上の警告です。
しかし、実際には Fragment や条件分岐を使っている場合など、原因がわかりにくいケースが少なくありません。

この記事では、この警告が出る根本的な理由と、状況別の解決方法を整理します。

警告の意味:React が子要素を区別できない

React はリストをレンダリングする際、どの要素が追加・削除・更新されたのかを判定するために、各子要素に一意の識別子(key)を求めます。

もし key が存在しない、あるいは重複している場合、React は正しく差分を判断できず、レンダリングの最適化が崩れる可能性があります。
そのため、React は次のような状況で警告を出します。

  • .map() で複数要素をレンダリングしている
  • 要素群の中で同じ key が使われている
  • Fragment や条件分岐によって構造が変化し、key の整合性が崩れている

よくある誤りと修正例

1. map の戻り値で key がない

典型的なケースです。

{items.map(item => (
  <div>{item.name}</div>
))}

この場合、React はどの <div> がどの item に対応しているのか判断できません。
修正はシンプルで、key を明示します。

{items.map(item => (
  <div key={item.id}>{item.name}</div>
))}

key兄弟要素の中で一意 であればよく、item.id のように安定した識別子を使うのが理想です。

2. index を key に使っている

一時的な対応として indexkey に使う例も見られます。

{items.map((item, index) => (
  <div key={index}>{item.name}</div>
))}

これはデータが固定されている場合は問題ありませんが、
要素の並び替え・削除・挿入がある場合には非推奨です。
React が再利用ロジックを誤り、意図しない描画が起きる可能性があります。

安定した識別子(例:IDやユニークキー)を使うようにしましょう。

3. Fragment 内の要素が key を持たない

複数の要素を <>...</> で囲んで返すときも、実は暗黙的に「配列のような構造」が作られています。
そのため、Fragment にも key が必要になる場合があります。

{list.map(item => (
  <>
    <h3>{item.title}</h3>
    <p>{item.description}</p>
  </>
))}

上記のように警告が出る場合は、明示的に key を付与します。

{list.map(item => (
  <React.Fragment key={item.id}>
    <h3>{item.title}</h3>
    <p>{item.description}</p>
  </React.Fragment>
))}

短縮構文 <> では key を指定できないため、<React.Fragment key={...}> を使う必要があります。

4. 条件分岐を含むリストレンダリング

条件によって要素が出たり消えたりする場合にも警告が発生します。

{items.map(item => (
  item.visible && <div key={item.id}>{item.name}</div>
))}

一見問題なさそうに見えますが、visible の状態が頻繁に変わると、
リストの構造が再計算されるたびに key の対応関係が崩れる可能性があります。

この場合も、常に安定した key を持たせておけば安全です。

{items
  .filter(item => item.visible)
  .map(item => (
    <div key={item.id}>{item.name}</div>
  ))}

「条件分岐は map の中ではなく、外で絞り込む」が基本の方針です。

5. ネストされたリスト構造で key が重複している

多段階のリストを扱う場合、上位と下位のリストで同じ key が重複していると警告が出ることがあります。

{groups.map(group => (
  <ul key={group.id}>
    {group.items.map(item => (
      <li key={group.id}>{item.name}</li>  // ← 重複
    ))}
  </ul>
))}

この場合、group.id ではなく、アイテム固有の識別子を使用します。

<li key={item.id}>{item.name}</li>

同一階層の要素で一意であれば問題ありません。

key が不要なケース

次のような場面では key は不要です。

  • 兄弟要素を持たない単一の JSX 要素
  • 条件分岐で単一要素を切り替えるだけのとき
  • map を使わずに直接要素を定義する場合

key は「リスト」または「兄弟間の識別」に必要なものと考えましょう。

まとめ

「Each child in a list should have a unique ‘key’ prop」警告は、
React が仮想DOMの差分を正しく追跡できないことを知らせるサインです。

要点

  • key はリスト内の兄弟要素間で一意である必要がある
  • index は一時的には使えるが、順序変更がある場合は避ける
  • Fragment や条件分岐の中でも key を忘れずに付ける
  • 短縮構文の Fragment (<>) では key が指定できない

この仕組みを理解しておけば、React の再レンダリング挙動をより正確に制御でき、警告に惑わされることはなくなります。

タイトルとURLをコピーしました