Next.jsで学ぶReact講座(完全初心者向け・30日) | 第5章:仕上げと次の一歩 – 簡単な設計練習

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

設計練習って何をやる時間か

ここまで、コードの書き方そのものはかなり身についてきています。
でも実務に近づくと、いきなり「この画面作って」と言われます。
つまり、「何をどう書くか」の前に、「どう分解して考えるか」が問われ始めます。

この章は、コードを書く前の「設計」を、小さな例で一緒に練習する時間です。

画面構成をざっくり決める
コンポーネントをどう分けるか決める
状態(state)がどこからどこへ流れるかをイメージする

この3つを、ミニアプリを題材にしながら練習していきます。


例題アプリを決める(シンプルな講座一覧ページ)

どんな画面を作るかを言葉で描く

いきなりコードじゃなく、「こんな画面を作りたい」を日本語で決めます。
例題として、「講座一覧ページ」を考えましょう。

画面上部にページタイトルと説明文がある。
その下に「検索ボックス」と「レベル選択(全部 / 初級 / 中級 / 上級)」が並ぶ。
検索条件に応じて、講座カードの一覧が絞り込まれて表示される。

もう少し具体的にすると、こんな感じです。

ページタイトル「講座一覧」
説明文「Next.js × React の講座一覧です。検索やレベルで絞り込めます。」
検索テキスト入力(タイトルに対する部分一致検索)
レベル選択のボタン(全部 / 初級 / 中級 / 上級)
講座カード(タイトル、レベル、簡単な説明)を複数表示

このレベルまで言葉で描ければ、設計の入り口には十分です。


画面構成を考える(「箱」で分けてみる)

大きな「箱」に分けるイメージ

ここでまだコードは書きません。
頭の中か紙の上で、「大きな箱」に画面を分けていきます。

ページ全体の枠(Page)
検索・絞り込みエリア
講座一覧エリア

もっと分けると、

ヘッダー(タイトル+説明)
フィルター(検索ボックス+レベルボタン)
リスト(カードの並び)
1枚のカード

このくらいの粒度がちょうどよいです。

重要なのは、最初から細かく分けすぎないことです。
「明らかに役割が違うまとまり」で区切る、くらいで止めておくと、後で楽になります。

Next.js 的な「ページ」としての位置づけ

例えば、URL は /lessons としましょう。
そうすると、Next.js のファイルは app/lessons/page.tsx になります。

この page.tsx の中に、

ページ全体の構成
フィルタと一覧をどう並べるか

という「画面の骨組み」を書き、その中でコンポーネントを分割していくイメージです。


コンポーネント設計(役割ごとに分ける)

コンポーネント候補を言葉で挙げる

さっきの「箱」を、そのままコンポーネント候補として書き出してみます。

LessonsPage … ページ全体(Next.js の page.tsx
LessonFilter … 検索ボックス+レベルボタン
LessonList … フィルタ済みの講座を並べる
LessonCard … 1件分の講座カード

ここまでを決めた段階では、まだ「正解・不正解」を考えなくていいです。
大事なのは、「どのコンポーネントが何を担当するか」をざっくり決めることです。

各コンポーネントの責務を一言で説明する

ここから少し実務思考っぽいことをやります。
各コンポーネントについて、「この子の仕事は何か」を一言で説明してみます。

LessonsPage
「全体の状態(講座データ・検索条件)を持ち、Filter と List をつなぐ親」

LessonFilter
「検索テキストとレベルの選択UIを表示し、『検索条件を変えたい』という意思を親に伝える」

LessonList
「渡された講座配列を、カードのリストに変換して表示する」

LessonCard
「講座1件分の情報を見栄えよく表示するだけ。状態は持たない」

こうやって責務をはっきりさせると、

どこが state を持つべきか
どこまでを props で受け取るだけにするか

が見えやすくなります。


状態の流れを描く(state をどこに置くか)

まず「何を状態として持つか」を列挙する

この画面に出てくる「変わりうるもの」は何かを洗い出します。

講座一覧データ(固定のダミーデータでも、将来APIから来るものでも)
検索テキスト
選択中のレベル

この3つは「変わりうる」「UI に影響する」ので、state の候補です。

次に、「誰がそれを使うか」を考えます。

講座一覧データ → LessonList が使う
検索テキスト → LessonFilter が編集し、LessonList が使う
選択中のレベル → LessonFilter が編集し、LessonList が使う

「複数のコンポーネントで使う」ものは、その共通の親に持たせるのが原則でした。
ここでは、共通の親は LessonsPage です。

つまり、state は全部 LessonsPage に置くのが自然です。

状態の流れを矢印でイメージする

言葉だけだとふわっとするので、頭の中で矢印を描いてみます。

LessonsPagelessons, searchText, selectedLevel の state がある。
LessonsPageLessonFilter
現在の searchTextselectedLevel を渡す。
さらに「検索条件が変わったら呼んでほしいハンドラ(onChangeSearch / onChangeLevel)」も渡す。

LessonFilter
input やボタンのイベントで、これらのハンドラを呼ぶだけ。

LessonsPage
searchTextselectedLevel に基づいて
filteredLessons を計算し、LessonList に渡す。

LessonList
受け取った lessons を map で LessonCard に変換するだけ。

このように、

状態を持つのは LessonsPage
LessonFilterLessonList は「表示とイベントのきっかけ担当」

という流れにしておくと、状態管理がだいぶスッキリします。


簡易コードで「設計→実装」の橋渡しをする

まずは型と state だけ書いてみる

完全なコードを書く前に、「設計どおりの形になっているか」を軽く確認します。

// app/lessons/page.tsx
"use client";

import { useState } from "react";

type Lesson = {
  id: number;
  title: string;
  level: "初級" | "中級" | "上級";
  description: string;
};

const allLessons: Lesson[] = [
  { id: 1, title: "React 入門", level: "初級", description: "コンポーネントの基礎" },
  { id: 2, title: "Next.js 基礎", level: "中級", description: "ページとルーティング" },
  { id: 3, title: "状態管理応用", level: "上級", description: "useReducer と設計" },
];

export default function LessonsPage() {
  const [searchText, setSearchText] = useState("");
  const [selectedLevel, setSelectedLevel] =
    useState<"all" | "初級" | "中級" | "上級">("all");

  const filteredLessons = allLessons.filter((lesson) => {
    const matchesText = lesson.title.includes(searchText);
    const matchesLevel =
      selectedLevel === "all" ? true : lesson.level === selectedLevel;
    return matchesText && matchesLevel;
  });

  return (
    <main>
      {/* ここに LessonFilter と LessonList を置く */}
    </main>
  );
}
TSX

この段階で、「状態の置き場所」と「絞り込みロジック」をまず固めています。
ここを先に作っておくと、UI 部分はあとからゆっくり肉付けできます。

コンポーネント間の props を設計する

次に、LessonFilterLessonList の props を型から決めます。

type LessonFilterProps = {
  searchText: string;
  selectedLevel: "all" | "初級" | "中級" | "上級";
  onChangeSearch: (value: string) => void;
  onChangeLevel: (value: "all" | "初級" | "中級" | "上級") => void;
};

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

ここで大事なのは、「何を受け取って、何を親に返すか」を明確にすることです。

LessonFilter
表示に必要な今の値(searchText / selectedLevel)
と、「新しい値」を親に渡すための関数(onChangeSearch / onChangeLevel)

LessonList
「もう絞り込み済み」の配列を受け取るだけ

という構造です。

この時点で、「状態の流れ」がかなり具体的になっています。


実務思考の入口としての設計練習

「いきなり書かない」という贅沢を自分に許す

実務に近づくほど、「すぐ書く人」と「一度立ち止まって考える人」の差が出ます。

すぐ書くと、最初の 30 分は早く進んでいるように見えるけれど、
1〜2 時間後に「やばい、構造が破綻してきた」となりがちです。

反対に、

画面の構成を言葉で整理する
コンポーネントの役割を一言で決める
状態の流れを軽く書き出す

この 10〜15 分を挟むだけで、
後半の迷い方がまったく変わります。

「設計練習」は、あなたにその“贅沢な時間”の取り方をインストールするステップです。

最初は「小さな画面」で訓練する

いきなり大きなダッシュボードの設計をしようとすると、確実に潰れます。
まずは、今回のような小さな画面からでいいです。

講座一覧
TODO一覧+フィルタ
プロフィール編集フォーム

など、「1ページで完結する」ものを題材にして、

画面を箱で分ける
コンポーネントを列挙する
状態の流れを矢印で描く

この 3 ステップを紙でも頭でもいいので毎回やってみる。
それが「実務思考の入口」です。


まとめと、あなたへの宿題

この章でやったことを言い直すと、こうです。

画面構成をコード前に言葉で描くことで、「何を作るのか」を自分に説明できるようにする。
コンポーネントは「役割が違うまとまり」で分け、各コンポーネントの責務を一言で説明してから実装に入る。
状態の流れは、「どの state を誰が使うか」「どこが最も近い共通の親か」を考え、親 → 子への props、子 → 親へのイベントという矢印で整理する。

小さな宿題として、もし余力があれば、

自分で「別の画面」を一つ決めて、
今やった流れ(画面の言語化 → コンポーネント分割 → 状態の流れ)を文章で書いてみてください。

例えば、「お気に入り記事一覧」でも、「簡易チャット画面」でもいいです。
その文章をそのまま見せてもらえれば、
「ここはこう分けるともっと楽になるよ」という、実務寄りのコメントもいくらでも返せます。

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