LoginSignup
9
2

More than 1 year has passed since last update.

React.jsでWarning: Each child in a list should have a unique "key" prop.が発生した時の原因と対処法

Last updated at Posted at 2022-09-29

スクリーンショット 2022-09-27 22.26.58.png
ReactやNext.jsで開発していると、スクショのようなwarningに遭遇したことあるかと思います。

原因

map関数内でhtmlタグをreturnする時に、一番上の要素にユニークなkeyを定義していないこと、もしくはkeyがユニークになってないことが原因です。
Reactで要素を一覧表示するときとかに、誰しも一度は発生するwarningではないのでしょうか。

具体的にこのようなコードの時に発生します。

1、keyを定義していない
const USER_LIST = [
    { id: 1, username: "hoge", age: 10 },
    { id: 2, username: "foo", age: 15 },
    { id: 3, username: "hoge", age: 21 },
    { id: 4, username: "react", age: 43 },
    { id: 5, username: "next", age: 5 },
  ];

 <ul>
    {USER_LIST.map((user, index) => (
      <li>
        <p>{user.username}</p>
        <p>{user.age.toString()}歳</p>
      </li>
    ))}
 </ul>
2、keyがユニークになってない(同じ値のkeyがある)
 <ul>
    {USER_LIST.map((user, index) => (
      <li key={user.username}>
        <p>{user.username}</p>
        <p>{user.age.toString()}歳</p>
      </li>
    ))}
 </ul>

※ちなみにkeyがユニークになってない時はこういったwarningになります。
スクリーンショット 2022-09-27 22.35.50.png

解決方法

原因に書いてある通りユニークなkeyを付与してないことが問題なので、ユニークなkeyを挿れます。

 <ul>
    {USER_LIST.map((user, index) => (
      <li
        key={user.id} //←ユニークなkey
        style={{ display: "flex", justifyContent: "start" }}
      >
        <p>{user.username}です。</p>
        <p>現在{user.age.toString()}歳です</p>
      </li>
    ))}
 </ul>

スクリーンショット 2022-09-28 1.37.16.png
無事表示されました!

ここからが本題

上記の方法で基本的には解決できますのが、
もしもmap関数内でreturnするhtmlタグが複数の時だったり、複数のタグをreturnするが、divタグなどで一つに囲いたくないことがたまにあるかと思います。

 <ul>
    {USER_LIST.map((user, index) => (
      <>
        <li
          key={user.id}
          style={{ display: "flex", justifyContent: "start" }}
        >
          <p>{user.username}です。</p>
          <p>現在{user.age.toString()}歳です</p>
        </li>
        {user.age >= 20 && (
          <p style={{ color: "red", fontWeight: "bold" }}>
            このかたは成人です
          </p>
        )}
      </>
    ))}
 </ul>

今回のケースとしては、もしユーザーの年齢が20歳以上だったら赤文字で「このかたは成人です」と表示させたいです。
liタグには入れたくないので、上の階層にhtmlタグを用意しますが、設計上divタグを使いたくないので空タグを用意しました。
ですので現状、空タグの中には二つhtmlタグが挿れられてます。

この状態でconsoleを開くと、、、

スクリーンショット 2022-09-28 1.46.32.png
エラーになってます。
解決方法を模索しました。

検証1 同じkeyを挿れる

新しく追加した特定の条件の時のみ表示されるpタグにも同様のkeyを挿れてみました。
liタグと同様にuser.idを挿れてみると、以前発生したような「同じkeyがあります」とエラーになってしまいましたので、これではないようです。

検証2 別のユニークなidを挿れる

次はuser.idとは別のユニークなidである、map関数の第二引数のindexを使いました。
再度consoleを開いてみると、、、

 <ul>
    {USER_LIST.map((user, index) => (
      <>
        <li
          key={user.id}
          style={{ display: "flex", justifyContent: "start" }}
        >
          <p>{user.username}です。</p>
          <p>現在{user.age.toString()}歳です</p>
        </li>
        {user.age >= 20 && (
          <p key={index} style={{ color: "red", fontWeight: "bold" }}>
            このかたは成人です
          </p>
        )}
      </>
    ))}
 </ul>

スクリーンショット 2022-09-28 1.49.47.png
またエラーになってます。。

原因

原因は「map関数内でreturnするhtmlタグの一番上の要素にkeyを定義していない」からです。
今回だと一番上の要素は空タグなので反映されないだろと思われますが、どうやらエラーになってしまうようです。
もちろん空タグなのでkeyを定義することは不可能です。

解決方法

解決方法としては、空タグを使うのではなく「React.Fragment」を使用することです。
空タグではkeyを定義することが不可能でしたが、同様の機能をするReact.Fragmentに変えるとkeyを定義することができます。
公式の参考サイト : https://ja.reactjs.org/docs/fragments.html

 <ul>
    {USER_LIST.map((user, index) => (
      <React.Fragment key={user.id}>
        <li style={{ display: "flex", justifyContent: "start" }}>
          <p>{user.username}です。</p>
          <p>現在{user.age.toString()}歳です</p>
        </li>
        {user.age >= 20 && (
          <p style={{ color: "red", fontWeight: "bold" }}>
            このかたは成人です
          </p>
        )}
      </React.Fragment>
    ))}
 </ul>

スクリーンショット 2022-09-28 1.54.57.png
無事warningが消えて解決できました!!!

warningが一つあるだけでもconsole.logのエリアに結構大きく表示されてしまうので、極力解決していきたいですね

9
2
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
9
2