8
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【初心者向け】next.jsにおけるmapの使い方と「Each child in a list should have a unique "key" prop」の対応

Last updated at Posted at 2021-12-29

#背景と実施したこと

next.jsにおけるコンポーネント間の値の受け渡し、およびJSXにおける繰り返し処理を実装してみたので
備忘として残します。

#目次

  1. 概要
  2. ナビゲーションメニューに配列を使う
  3. 「Each child in a list should have a unique "key" prop」 の解消

##概要

  • ヘッダーメニューを作る際は配列を用いてJSXを活かした実装にしよう。
  • jsxにはfor文はないので、mapを使って「繰り返し」を実装しよう。
  • mapを使用する際は「key」を指定して警告を解除しよう。

##ナビゲーションメニューに配列を使う

以下の画像のようなヘッダーメニューを作るときにはメニューの中身を直接HTMLに記載するより
js上の配列に格納しておく方が、保守性の観点からも望ましいでしょう。
(せっかくnext.js,Reactを使っているならJSX記法も活用した方がいい)

image.png

ということで、もともとは以下のような実装だったものをJSXを活用したものに変更していきます。

■変更前

header.js
export default function Header() {
  }

  return (
    <nav className='flex'>
      <div className='flex-none  sm:flex-1 md:flex-1 lg:flex-1 xl:flex-1'>
        <Link href='/'>
          <a>
            <Image src='/images/logo.png' alt='logo' width={200} height={100} />
          </a>
        </Link>
      </div>
      <div className='flex-initial  text-[#abc5c5] font-bold	m-5'>
        <ul className='md:flex  hidden flex-initial text-left'>
          <li className='p-4'>
            <button className='font-bold'>about</button>
          </li>
          <li className='p-4'>
            <button className='font-bold'>skills</button>
          </li>
          <li className='p-4'>
            <button className='font-bold'>values</button>
          </li>
          <li className='p-4'>
            <button className='font-bold'>future</button>
          </li>
        </ul>
      </div>
    </nav>

###親コンポーネントから値を渡す

今回はHeaderコンポーネントを呼び出す親コンポーネントに配列を持たせました。
これは別にHeaderコンポーネントの中にあっても問題ないと思うのですが、(というかヘッダーの情報はヘッダーに集約した方が本来的にはよいはず)
コンポーネント間の値の受け渡しの確認もしたかったので、そういう実装にしています。

menuListという配列の各要素にnameというキーをもたせて連想配列を格納しています。
キーがnameしかないので連想配列でなくてもよいのですが、何かしらのキーの追加が今後あるかもしれないのでこうしています。

そしてHeaderコンポーネントを呼び出す部分で、listという名称にmenuListを入れて、子コンポーネントに渡します。

layouut.js
export default function Layouut() {
  const menuList = [
    { name: 'about' },
    { name: 'skills'},
    { name: 'values'},
    { name: 'future'}
  ];

  return (
    <>
      <Header list={menuList}></Header>
      <main>
        <Main />
        <About />
        <Skill />
        <Values />
        <Future />
      </main>
      <Footer />
    </>
  );
}


###子コンポーネント側で親からもらった値を参照する

子コンポーネント側ではpropsという引数を指定しており、
dataというconstの引数に一度格納しています。
ulタグの中でJSX記法によってdataを活用します。

「リストの要素の数だけメニューを表示する」という繰り返しを実現するためにはmap関数を活用します。
※for文は使えない

これで配列の要素の数だけメニューを表示することはできます。

header.js
export default function Header(props) {
  
  const data = props.list;

  
  return (
    <nav className='flex'>
      <div className='flex-none  sm:flex-1 md:flex-1 lg:flex-1 xl:flex-1'>
        <Link href='/'>
          <a>
            <Image src='/images/logo.png' alt='logo' width={200} height={100} />
          </a>
        </Link>
      </div>
      <div className='flex-initial  text-[#abc5c5] font-bold	m-5'>
        <ul className='md:flex  hidden flex-initial text-left'>
          {data.map((value) => (
            <li className='p-4'>
              <button className='font-bold'>{value.name}</button>
            </li>
          ))}
        </ul>
      </div>
    </nav>
  );
}

「Each child in a list should have a unique "key" prop」 の解消

しかしF12でコンソールを見てみると警告が出ています。
image.png

これはリストの中身が削除や追加などの変更があった際に、どの項目が変わったかを判断するためにkey(配列の要素の何番目か)という情報を入れることを推奨しているからです。

そのためコードを以下のように変更します。
※mapの第2引数にindexを付与して、liタグの中にkeyを指定しています。
これで警告は消えます。

header.js
export default function Header(props) {
  
  const data = props.list;

  
  return (
    <nav className='flex'>
      <div className='flex-none  sm:flex-1 md:flex-1 lg:flex-1 xl:flex-1'>
        <Link href='/'>
          <a>
            <Image src='/images/logo.png' alt='logo' width={200} height={100} />
          </a>
        </Link>
      </div>
      <div className='flex-initial  text-[#abc5c5] font-bold	m-5'>
        <ul className='md:flex  hidden flex-initial text-left'>
          {data.map((value, index) => (
            <li key={index} className='p-4'>
              <button className='font-bold'>{value.name}</button>
            </li>
          ))}
        </ul>
      </div>
    </nav>
  );
}

##まとめ
変更前と変更後を比べてみるとコードがすっきりしたことがわかりますね。

■Before

header.js
export default function Header() {
  }

  return (
    <nav className='flex'>
      <div className='flex-none  sm:flex-1 md:flex-1 lg:flex-1 xl:flex-1'>
        <Link href='/'>
          <a>
            <Image src='/images/logo.png' alt='logo' width={200} height={100} />
          </a>
        </Link>
      </div>
      <div className='flex-initial  text-[#abc5c5] font-bold	m-5'>
        <ul className='md:flex  hidden flex-initial text-left'>
          <li className='p-4'>
            <button className='font-bold'>about</button>
          </li>
          <li className='p-4'>
            <button className='font-bold'>skills</button>
          </li>
          <li className='p-4'>
            <button className='font-bold'>values</button>
          </li>
          <li className='p-4'>
            <button className='font-bold'>future</button>
          </li>
        </ul>
      </div>
    </nav>

■After

header.js
export default function Header(props) {
  
  const data = props.list;

  
  return (
    <nav className='flex'>
      <div className='flex-none  sm:flex-1 md:flex-1 lg:flex-1 xl:flex-1'>
        <Link href='/'>
          <a>
            <Image src='/images/logo.png' alt='logo' width={200} height={100} />
          </a>
        </Link>
      </div>
      <div className='flex-initial  text-[#abc5c5] font-bold	m-5'>
        <ul className='md:flex  hidden flex-initial text-left'>
          {data.map((value, index) => (
            <li key={index} className='p-4'>
              <button className='font-bold'>{value.name}</button>
            </li>
          ))}
        </ul>
      </div>
    </nav>
  );
}

今回はメニューということで動的になることはないと思いますが、配列などの変数を活用してHTMLを作っていくのは動的なサイトを実現したい場合は必須のテクニックです。
繰り返しの要素がある場合はmapを使う、ということと合わせて覚えていきましょう。

8
6
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
8
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?