Next.jsで学ぶReact講座(完全初心者向け・30日)演習問題・課題 | 第2章:Reactの基本 – ミニアプリ①

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

この課題のねらい

ここまでの「useState」「props」「コンポーネント分割」をぜんぶまとめて、小さな「カウンターアプリ」を完成させます。
単なる練習問題ではなく、

state で「今の値」を持つ
props で「外から設定できる部分」を渡す
親コンポーネントと子コンポーネントの役割を分ける

この3つを一気に体で覚えるためのミニアプリです。


必須課題 カウンターアプリ完成(stateとpropsを使用)

アプリの構成イメージ

まず、シンプルな構成から始めます。

親コンポーネント(ページ)
子コンポーネント(カウンター本体)

親は「画面全体の枠」と「タイトル表示」を担当し、
子は「カウント表示とボタン」を担当します。

ここでは、「初期値は子の中で 0 に固定」というバージョンで説明します(このあと挑戦課題で props にします)。

子コンポーネント:カウンター本体

まずはカウンターそのもののコンポーネントです。

// app/components/Counter.tsx
"use client";

import { useState } from "react";

export default function Counter() {
  const [count, setCount] = useState(0);

  function handleIncrement() {
    setCount((prev) => prev + 1);
  }

  function handleDecrement() {
    setCount((prev) => prev - 1);
  }

  return (
    <section style={{ border: "1px solid #e5e7eb", padding: "16px", borderRadius: "8px" }}>
      <p style={{ fontSize: "20px", marginBottom: "12px" }}>現在のカウント: {count}</p>

      <div style={{ display: "flex", gap: "8px" }}>
        <button onClick={handleIncrement}>+1</button>
        <button onClick={handleDecrement}>−1</button>
      </div>
    </section>
  );
}
TSX

ここで重要なのは次のポイントです。

useState(0) で、このコンポーネントが「count という状態を持つ」ことを宣言している。
handleIncrement / handleDecrement の中で setCount を呼び、前の値から新しい値を計算している。
JSX で {count} を使い、state の値をそのまま画面に出している。

「ボタンを押す → state を更新 → 再描画されて count 表示が変わる」という React の黄金パターンが詰まっています。

親コンポーネント:ページとして表示

次に、このカウンターをページから使います。

// app/page.tsx
import Counter from "./components/Counter";

export default function CounterPage() {
  return (
    <main style={{ padding: "24px", fontFamily: "sans-serif" }}>
      <h1>ミニカウンターアプリ</h1>
      <p style={{ marginBottom: "16px" }}>
        ボタンを押してカウントアップ・カウントダウンしてみましょう。
      </p>

      <Counter />
    </main>
  );
}
TSX

ここで使っているのが「props ではないが、親子の関係」です。

親(ページ)は「ここにカウンターを置く」と決めるだけ。
子(Counter)は「実際の動きと状態管理」を担当する。

この段階でも、「ページはレイアウト・説明文、Counter は機能本体」という役割分担ができています。


挑戦課題 初期値をpropsで指定(stateとpropsの組み合わせ)

「初期値を外から変えられるカウンター」にする

次のステップは、

カウンターの初期値を、親コンポーネントから props で渡せるようにする

です。

やりたいことは、例えばこういう使い方です。

1つ目のカウンターは 0 からスタート
2つ目のカウンターは 10 からスタート

つまり、同じ Counter コンポーネントを「違う初期値」で再利用します。

Counterコンポーネントに初期値propsを追加

まず、Counter に initialValue という props を追加します。

// app/components/Counter.tsx
"use client";

import { useState } from "react";

type CounterProps = {
  initialValue: number;
};

export default function Counter({ initialValue }: CounterProps) {
  const [count, setCount] = useState(initialValue);

  function handleIncrement() {
    setCount((prev) => prev + 1);
  }

  function handleDecrement() {
    setCount((prev) => prev - 1);
  }

  return (
    <section style={{ border: "1px solid #e5e7eb", padding: "16px", borderRadius: "8px" }}>
      <p style={{ fontSize: "20px", marginBottom: "12px" }}>現在のカウント: {count}</p>

      <div style={{ display: "flex", gap: "8px" }}>
        <button onClick={handleIncrement}>+1</button>
        <button onClick={handleDecrement}>−1</button>
      </div>
    </section>
  );
}
TSX

重要ポイントを整理します。

CounterProps 型で「このコンポーネントは initialValue: number を受け取ります」と宣言。
引数で { initialValue }: CounterProps と分割代入し、props から初期値を取り出している。
useState(initialValue) にすることで、「初期状態の count を props から決める」ようにした。

ここが state と props を組み合わせる一番おいしいポイントです。

親から初期値を渡して使う

次に、ページ側をこう書き換えます。

// app/page.tsx
import Counter from "./components/Counter";

export default function CounterPage() {
  return (
    <main style={{ padding: "24px", fontFamily: "sans-serif" }}>
      <h1>ミニカウンターアプリ</h1>
      <p style={{ marginBottom: "16px" }}>
        初期値の違うカウンターを並べてみます。
      </p>

      <section style={{ display: "flex", gap: "16px" }}>
        <div>
          <h2>カウンターA(初期値 0)</h2>
          <Counter initialValue={0} />
        </div>

        <div>
          <h2>カウンターB(初期値 10)</h2>
          <Counter initialValue={10} />
        </div>
      </section>
    </main>
  );
}
TSX

ここで実現しているのは、

同じ Counter コンポーネントを
initialValue={0}initialValue={10} で 2 回呼び出している
それぞれ別々の state を持ち、独立して動く

という状態です。

これができると、「一つのコンポーネントを、条件や設定を変えながら再利用する」という React らしい設計が見えてきます。


stateとpropsの役割の整理(ここが超重要)

このミニアプリで特に深掘りしておきたいのは、「state と props の違い」です。

propsは「外から渡される設定」

Counter にとっての initialValue は、

どのページから呼ばれても
親が「今回はこの初期値でお願い」と指定する値

です。

Counter 自身は「その値がどこから来たか」は気にせず、
「最初の count をいくつにするか」という設定として使うだけです。

stateは「コンポーネント自身が持つ変化する値」

一方で、count は完全に Counter の内側の話です。

ボタンを押すたびに変わる
親から直接いじられることはない
Counter の中だけで完結して管理される

という性質をもっています。

言い換えると、

props は「最初の条件や設定」
state は「それに基づいて、コンポーネントが内部で変えていく値」

このイメージを持てると、もっと複雑なコンポーネントでも、

どこまでを props として外から渡すか
どこからを state として中に閉じ込めるか

を自分で判断できるようになっていきます。


まとめと、次の一歩のヒント

この「ミニカウンターアプリ」でやったことを整理すると、こうなります。

カウンター用のコンポーネントを作り、useState で count を持たせた。
親コンポーネント(ページ)から呼び出し、画面に表示した。
props(initialValue)を追加し、「初期値だけ外から指定」できるようにした。
同じ Counter コンポーネントを別の初期値で複数個並べて再利用した。

ここまでできていれば、React の超重要コンボである

「stateで動きを作る」+「propsで設定を渡す」+「コンポーネント分割で責務を分ける」

が、かなりしっかり体に入り始めています。

もし余力があれば、次のようなアレンジにも挑戦してみてください。

最大値・最小値を props で渡し、それを超えたらそれ以上増減しないカウンター
ラベル(例:「いいね数」「アクセス数」など)を props で渡して、表示テキストを変えられるようにする

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