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】keyを設定しているのに『Warning: Each child in a list should have a unique “key” prop.』エラーが発生したケースとその対策

Posted at

発生した事象

Reactでコードを書いていた際、Warning: Each child in a list should have a unique “key” prop.というエラーが発生しました。

このエラーは「各々のchildには必ずユニークなkeyの値を持つこと」という意味であり、多くの場合はkeyの値を設定することで解消することができます。

simple__sample.tsx
const items = [
    {id: 1, title: "item1"},
    {id: 2, title: "item2"},
    {id: 3, title: "item3"},
];

return (
    <div>
      {items.map(elem => {
        return(
         {/* ここに値が一意に定まるkey値を設定しないとエラーになる */}
          <div key={elem.id}> 
            <p>{elem.title}</p>
          </div>
        )
      })}
    </div>
  );

ですが、今回遭遇したケースではkey値を設定しているのにも関わらずEach child in a list should have a unique “key” prop.エラーが発生しました。

complex__sample.tsx
{/* solutionDataはデータベースから取得した値を配列化したもの */}
{solutionData.map((elem) => {
  return (
    <>
      <div key={elem.id} id={elem.id}>
        <div>
          <div className="flex">
            <p>{cdate(elem.created_at).format("YYYY-MM-DD")}</p>
            <p>{elem.practice_category}</p>
          </div>
          <p>{elem.practice_text}</p>
        </div>
        <div>
          {elem.video_id && (
            <div className="px-2 py-4">
              <iframe
                id="player"
                width="100%"
                src={"https://www.youtube.com/embed/" + elem.video_id} //先ほど保存したvideoId
                allowFullScreen
                className="aspect-16/9"
              />
            </div>
          )}
        </div>
      </div>
    </>
  );
})};

原因

上記のサンプルではreturnの一番外側の要素がkey値を持っていないことが原因。
<></>を使っている場合でもkeyはmapメソッドの一番外側で設定しないといけませんでした。

また、<></>の中にkeyを設定してもエラーとなります。

key をフラグメントに渡したい場合は、<>...> 構文を使用することはできません。

対策

①mapメソッドの一番外側のdivタグにkeyを設定する

mapメソッドの内側が並列な<div>で囲まれていない場合はこの方法が一番シンプルで分かりやすい。

先ほどの例で言うと以下のような形になる。

solution1.tsx
{solutionData.map((elem) => {
  return (
    {/* この箇所にあった<></>を削除。これでもHTML構造的にkeyは成立する */}
      <div key={elem.id} id={elem.id}>
        <div>
          <div className="flex">
            <p>{cdate(elem.created_at).format("YYYY-MM-DD")}</p>
            <p>{elem.practice_category}</p>
          </div>
          <p>{elem.practice_text}</p>
        </div>
        <div>
          {/* 動画が入る部分。今回の本筋に関係ないため省略 */}
        </div>
      </div>
  );
})};

<></><Fragment></Fragment>に変更する

React公式でも推奨されている方法。

<></>となっている部分を明示的に<Fragment>に書き直すことでkey値を設定することができる。
key値は<Fragment>タグの内側に書く。

key: 明示的な 構文で宣言されたフラグメントは key を持つことができます。
'react' から Fragment を明示的にインポートし、... とレンダーしなければなりません。

<></>の内側が並列なdivタグになっているような、<></>を使わないと支障が出るケースで特に有効。

complex__sample.tsx
import { Fragment } from "react";

{solutionData.map((elem) => {
  return (
  {/* ここの空フラグメントをFragmentに変更する */}
    <Fragment key={elem.id}>
      <div id={elem.id}>
        <div>
          <div className="flex">
            <p>{cdate(elem.created_at).format("YYYY-MM-DD")}</p>
            <p>{elem.practice_category}</p>
          </div>
          <p>{elem.practice_text}</p>
        </div>
        <div>
          {/* 動画が入る部分。今回の本筋に関係ないため省略 */}
        </div>
      </div>
    </Fragment>
  );
})};

①、②いずれかの方法を取れば「keyを設定しているのにエラーが出る」という現象を回避できるはずです。

最後に

今回発生したエラーは私の中でハマる頻度が多く、何回も繰り返す割に解決策が見つからないものでした。
同じようなエラーが発生した際に解決の糸口になれば幸いです。

参考文献・記事

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?