Next.jsで学ぶReact講座(完全初心者向け・30日)演習問題・課題 | 第2章:Reactの基本 – コンポーネント分割

React Next.js
スポンサーリンク

この課題のねらい

この演習のテーマは「コンポーネント分割」と「props を使った汎用化」です。
つまり、こういう流れを体で覚えてもらうことが目的です。

最初は 1 つのコンポーネントにベタ書き
リスト表示部分を別コンポーネントとして切り出す
さらに props を使って、どんな配列にも使い回せる「汎用コンポーネント」に近づける

この感覚が身につくと、「あとから整理できる人」になれます。実務ではこれがとても大事です。


必須課題 リスト表示部分を別コンポーネント化

分割前の「ベタ書き」バージョン

まずは「分割前」の状態を見てみましょう。
講座タイトルの配列をそのままページコンポーネントに書いている例です。

"use client";

const lessons = [
  "Reactの基本",
  "Next.jsのページとルーティング",
  "useStateとイベント処理",
];

export default function LessonsPage() {
  return (
    <main style={{ padding: "24px", fontFamily: "sans-serif" }}>
      <h1>講座一覧</h1>

      <ul style={{ marginTop: "12px" }}>
        {lessons.map((title) => (
          <li key={title}>{title}</li>
        ))}
      </ul>
    </main>
  );
}
TSX

この時点では、機能としては全く問題ありません。
でも、ページがもっと複雑になってくると、LessonsPage の中がどんどん長くなっていきます。

そこで、「リスト表示部分だけ」を別コンポーネントに切り出します。

リスト表示を別コンポーネントにする

まずは「ページの中に定義するだけ」の分割をしてみます。
ファイルは同じ app/lessons/page.tsx のままで OK です。

"use client";

const lessons = [
  "Reactの基本",
  "Next.jsのページとルーティング",
  "useStateとイベント処理",
];

function LessonList() {
  return (
    <ul style={{ marginTop: "12px" }}>
      {lessons.map((title) => (
        <li key={title}>{title}</li>
      ))}
    </ul>
  );
}

export default function LessonsPage() {
  return (
    <main style={{ padding: "24px", fontFamily: "sans-serif" }}>
      <h1>講座一覧</h1>
      <LessonList />
    </main>
  );
}
TSX

ここでやったことはシンプルです。

LessonList というコンポーネントを作り、その中に ulmap の処理を移した。
ページ側は <LessonList /> を呼び出すだけにして、スッキリさせた。

この時点では LessonList が外部の lessons 変数を直接参照しているため、まだ「完全な汎用コンポーネント」とは言えませんが、
「ページの責務」と「リスト表示の責務」が分かれただけでもだいぶ読みやすくなっています。


挑戦課題 propsを使って汎用化

「このコンポーネントは何をもらえれば仕事できるか」を考える

次のステップは、「リスト表示コンポーネントに必要な情報を props で渡す」ことです。
今の LessonList はファイルの外側にある lessons に依存しているので、他のページでは使いにくい状態です。

ここで一度、問い直します。

LessonList は、何を渡してもらえれば仕事ができるコンポーネントか。

答えはシンプルで、「表示したい文字列の配列」があれば仕事ができます。
なので、LessonList の props として「配列」を受け取るようにします。

文字列配列を受け取る汎用リストにする

まずは「文字列の配列」を表示する汎用リストコンポーネントにしてみましょう。

"use client";

type StringListProps = {
  items: string[];
};

function StringList({ items }: StringListProps) {
  return (
    <ul style={{ marginTop: "12px" }}>
      {items.map((item) => (
        <li key={item}>{item}</li>
      ))}
    </ul>
  );
}

const lessons = [
  "Reactの基本",
  "Next.jsのページとルーティング",
  "useStateとイベント処理",
];

export default function LessonsPage() {
  return (
    <main style={{ padding: "24px", fontFamily: "sans-serif" }}>
      <h1>講座一覧</h1>
      <StringList items={lessons} />
    </main>
  );
}
TSX

ここでの変更点を整理します。

StringListProps 型で「props に items: string[] を受け取る」と定義した。
StringList コンポーネントは items を受け取り、それを map して li に変換している。
ページ側では <StringList items={lessons} /> と書いて、配列を渡している。

これで StringList は、「どんな文字列配列でも表示できる汎用リスト」として他のページからも使い回せるようになりました。

オブジェクト配列を受け取るリストの汎用化イメージ

実務寄りにするなら、「オブジェクトの配列」を受け取って表示するコンポーネントもよく使います。
すこしだけレベルを上げて、Lesson 型の配列を受け取るコンポーネントも考えてみましょう。

type Lesson = {
  id: number;
  title: string;
};

type LessonListProps = {
  lessons: Lesson[];
};

function LessonList({ lessons }: LessonListProps) {
  return (
    <ul style={{ marginTop: "12px" }}>
      {lessons.map((lesson) => (
        <li key={lesson.id}>{lesson.title}</li>
      ))}
    </ul>
  );
}

const lessonsData: Lesson[] = [
  { id: 1, title: "Reactの基本" },
  { id: 2, title: "Next.js入門" },
  { id: 3, title: "状態管理の基礎" },
];

export default function LessonsPage() {
  return (
    <main style={{ padding: "24px", fontFamily: "sans-serif" }}>
      <h1>講座一覧</h1>
      <LessonList lessons={lessonsData} />
    </main>
  );
}
TSX

ここでの大事なポイントは、次の通りです。

「リスト表示の責務」は LessonList に閉じ込めた。
「どんなデータを表示するか」は親コンポーネント(LessonsPage)が決めて、props で渡している。
key は lesson.id にして、配列の中で一意になるものを使っている。

こうしておくと、LessonList を他のページからも呼び出せるし、
lessonsData を API から取得したデータに差し替えるのも簡単になります。


コンポーネント分割で意識してほしいこと

最後に、この課題で身につけてほしい感覚をまとめます。

まず、「長くなってきた JSX の一部を、名前付きコンポーネントに切り出す」という第一歩。
次に、「そのコンポーネントが何を渡されれば仕事できるか」を考えて、props として受け取る形にする。
最後に、「データを持つのは親、表示ロジックは子」という役割分担を意識する。

この流れが自然にできるようになると、

一覧部分だけを専用コンポーネントに切り出す
カード表示を再利用できるコンポーネントにする
フォームの入力欄を汎用コンポーネントにする

といった「実務でよくある分割」が、かなりスムーズにできるようになります。

もし余力があれば、今まで作った「講座リスト」や「TODOリスト」を題材にして、

リスト部分を専用コンポーネントに切り出す
配列を props で渡す

ところまで一度やってみてください。

タイトルとURLをコピーしました