1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Reactで出る『unique key』警告の原因と解決方法

Posted at

初めに

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(=> ( ... ) を使うと安全です。


ありがち落とし穴

  • indexkey に使う
    並べ替え・挿入・削除が発生するリストでは不適切。要素の state がズレたり、不要な再マウントが頻発する原因になる。

  • ランダム値やタイムスタンプを key に使う
    毎回変わる=React にとって「毎回別の要素」扱い。入力中の値やフォーカスがリセットされる。

  • 短縮フラグメント <>...</>
    key を付けられないため、必要な場合は React.Fragment を明示的に使う。

  • 複数配列を結合して描画する
    例:[...A.map(), ...B.map()]
    同じ親配列に並ぶため key が衝突しやすい。
    A:${id}B:${id} のようにプレフィックスを付けて区別する。

  • 子コンポーネント内部の .map() を見落とす
    親で key を付けても、子の配列レンダーに key が無ければ同様に警告が出る。


デバッグ手順(汎用)

  1. map / return [ / <> を全文検索
    → 各配列レンダーの 最上位要素に key があるかを確認する。

  2. 短縮フラグメントを見直す
    <>...</> を使っている場合は React.Fragment に置き換え、必要なら key を付与する。

  3. ヘルパー関数の返り値を確認
    関数が直接 JSX を返しているなら、関数コンポーネント化して <Helper ... key={...} /> の形にする。

  4. 配列結合を点検
    [..., ...] で複数の配列を展開している場合、プレフィックス付きのキー(例:A:${id} / B:${id})で一意化する。

  5. ESLint を活用
    ルール "react/jsx-key": "error" を有効化して、自動的に検出できるようにする。

  6. React DevTools で確認
    Components タブから要素を選び、key が付与されているか・値が安定しているかを目視チェックする。


ベストプラクティス

  • key安定・一意・不変な値を使う
    (例:DBの id、永続的なコードなど)
  • .map() が返す最上位要素に必ず key を付与する
    子要素や孫要素に付けても効果はない
  • Fragment を使うときは <React.Fragment key="..."> を利用する
  • 複数系列の配列を同じ親に並べるときは プレフィックスを付与して衝突回避
  • index は最終手段
    (並び順が固定されていて、配列が変化しない場合にのみ利用可)

まとめ

  • この警告の本質は 「兄弟要素の配列レンダーには、最上位に安定した key が必須」 ということ。

  • 対処に迷ったら:

    • 最上位に key を付ける
    • Fragment は React.Fragment を使う
    • 複雑になったらコンポーネント化する
  • 仕上げに ESLintReact DevTools を組み合わせれば、再発を防ぎつつ安心して開発できる。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?