できない.dev

React で「Each child in a list should have a unique key prop」警告が消せない

リスト描画の要素に一意な key を付けないと React は再利用判定に失敗し警告を出す。
配列インデックスは最終手段にして、データ由来の安定 ID を使うのが基本。
Fragment にも key を付ける場合は明示形を使う。

公開: 更新:

実行例あり(2026-08-20 に実環境で検証)

要約

Each child in a list should have a unique "key" prop の警告は、map などで生成した兄弟要素に 一意な key が付いていない ことを React が指摘している状態。key は再レンダリング前後で要素を同一視するための識別子で、データ由来の安定した ID を渡すのが基本。
配列インデックスは「並び順が変わらない」前提でのみ許容する最終手段。

実行例

実際に上記の手順を node:20 で動かすと、key を渡していない bad.js でも <ul> の描画自体は成功し、終了コードは 0 のまま警告だけが出る。
データ由来の id を key に渡した solve_key.js と、Fragment に key を持たせた solve_fragment.js では、その警告が出なくなる。

$ node bad.js
Warning: Each child in a list should have a unique "key" prop.
 
Check the top-level render call using <ul>. See https://reactjs.org/link/warning-keys for more information.
    at li
    at App
<ul><li>A</li><li>B</li></ul>
$ echo $?
0
$ node solve_key.js
<ul><li>A</li><li>B</li></ul>
$ echo $?
0
$ node solve_fragment.js
<dl><dt>a</dt><dd>A</dd><dt>b</dt><dd>B</dd></dl>
$ echo $?
0

— 2026-08-20 時点の出力

検証環境

検証日
実行環境
node:20Debian GNU/Linux 12 (bookworm)
バージョン
  • Node.js 20.20.2
  • npm 10.8.2
  • Python 3.11.2
  • Git 2.39.5

この記事の「実行例」は、上記の環境で実際にコマンドを実行して得られた出力をそのまま掲載しています。 再現手順はリポジトリの検証スクリプトとして管理し、定期的に再実行して出力を更新しています。

よくある原因

  1. map で key を渡していない: items.map(item => <li>{item}</li>) のように key を書き忘れている。
  2. index を key にしているが並び替える: 並び替え・追加・削除が起きるリストで key={i} を使うと、React は別物の要素同士を再利用してしまい、入力値や DOM 状態が他行に紛れる。
  3. 毎レンダリングで生成した ID: key={Math.random()} や render 内で crypto.randomUUID() を呼ぶと、毎回違う値になり再利用が一切効かない(実質 key 無しと同じ)。
  4. Fragment shorthand に key を付けられない: <>...</> 構文には属性が書けない。key が必要なら <Fragment key={id}>...</Fragment> を使う。

解決策

1. データ由来の id を使う

{users.map(u => (
  <li key={u.id}>{u.name}</li>
))}

id がサーバ側で割り当てられているなら最も安定する。key は同じ親内で一意であれば十分で、グローバルにユニークである必要はない(公式のリスト描画ドキュメント(新しいタブで開く))。

2. index は静的リスト限定

並び替えや挿入が無いと確信できる場面のみ index を許容する。

{labels.map((label, i) => (
  <Tag key={i}>{label}</Tag>
))}

並び替えが入ったら別の安定 ID に切り替える。

3. id を生成するなら親で 1 度だけ

const [items, setItems] = useState(() =>
  raw.map(r => ({ ...r, _id: crypto.randomUUID() }))
);

useState のイニシャライザで一度だけ計算する。
子コンポーネントの render 中に randomUUID を呼ばない。

4. Fragment に key を付ける

import { Fragment } from "react";
 
{rows.map(r => (
  <Fragment key={r.id}>
    <dt>{r.label}</dt>
    <dd>{r.value}</dd>
  </Fragment>
))}

公式の Fragment ドキュメント(新しいタブで開く) のとおり、<></> の shorthand には属性を書けないため、key が必要なときだけ Fragment の明示形に切り替える。

この記事は役立ちましたか?