🐸

React 基本: リスト要素と複合コンポーネント

2024/03/20に公開

複合コンポーネントとは

複合コンポーネントは、小さい単純なコンポーネントを組み合わせ、ひとつのコンポーネントを作る技術。例えば、ボタン/入力フォーム/コンポーネントを組み合わせて商品の注文フォームを作ったり、タイトル/本文/フッターコンポーネントを組み合わせてカードコンポーネントを作ったり。

今回は、単純化したページ内アンカーリンクのリストコンポーネントををサンプルとして説明する。

単一コンポーネントの場合

TocLink コンポーネント側でリンク一式の配列データを受け取り、各子要素を map 関数のループ処理で組み立てる

components/TocLink.tsx
function TocLink(data: { id: string; title?: string }[]) {
  return (
    <ul className="XXX">
      {data.map(({ id, title }) => {
        return (
          <li key={id}>
            <a className="YYY" href={`#${id}`}>
              {title ? title : id}
            </a>
          </li>
        )
      })}
    </ul>
  )
}

export default TocLink

使用するページでリンクのデータ (オブジェクトの配列) 一式を用意し、TocLink コンポーネントに props として渡す。

access/page.tsx
import TocLink from '@/components/TocLink'

export default function Page() {
  const TocData = [
      {id: 'google-map', title: 'Google マップ'},
      {id: 'access-by-train', title: '電車でのアクセス'},
      {id: 'access-by-car', title: '車でのアクセス'},
  ]

  return (
      <TocLink data={TocData} />
  )
}

出力 HTML

<ul class="XXX">
    <li><a class="YYY" href="#google-map">Google マップ</a></li>
    <li><a class="YYY" href="#access-by-train">電車でのアクセス</a></li>
    <li><a class="YYY" href="#access-by-car">車でのアクセス</a></li>
</ul>
  • データと使用箇所を別の場所で管理するので、コード量が増えるとわかりづらくなる。
  • 同じデータを複数箇所で使う場合や外部データを読み込む場合はシンプルに実装できる。

複合コンポーネントにした場合

ul 要素と li 要素用のコンポーネントを別々に用意し組み合わせる。出力 HTML は単一コンポーネントのサンプルのものと同じ。

components/TocLink.tsx
import React from 'react'

function TocLink({ children }: { children: React.ReactNode }) {
  return (
    <ul className="XXX">
      {children}
    </ul>
  )
}

export function TocLinkItem({
  id,
  children,
}: {
  id: string
  children: string
}) {
  return (
    <li key={id}>
      <a className='YYY' href={`#${id}`}>
        {children}
      </a>
    </li>
  )
}

export default TocLink

使用ページでは、各リンクのデータを子要素の props として渡す。
リスト項目は children としてタグ内に書くようにすると直感的。
使用箇所に直接データを書くので見通しが良い。

access/page.tsx
import TocLink, { TocLinkItem } from '@/components/TocLink'

export default function Page() {
  return (
      <TocLink>
        <TocLinkItem id='google-map'>Google マップ</TocLinkItem>
        <TocLinkItem id='access-by-train'>電車でのアクセス</TocLinkItem>
        <TocLinkItem id='access-by-car'>車でのアクセス</TocLinkItem>
      </TocLink>
  )
}

まとめ

  • リスト要素のように親子で構成されたタグで子要素をループ処理をする場合、複合コンポーネントとすると見通しがよく扱いやすい。
  • 同じデータを複数箇所で使う場合や外部データを読み込む場合は、単一コンポーネントの方がシンプルに実装できる。
GitHubで編集を提案

Discussion