この課題のねらい
この演習は「useState を使って、画面が“動く”体験を自分の手で作る」のがテーマです。
やることはシンプルですが、React の本質がギュッと詰まっています。
useState で state(状態)を持つ
ボタンを押したときに state を更新する
state の値を JSX に表示して、変化が見えるようにする
ここができれば、「静的なページ」から「インタラクティブなアプリ」へ一段階進めます。
必須課題① カウントアップボタン作成
useStateで「今の数値」を覚えておく
まずは、1 ページにシンプルなカウンターを作ってみましょう。app/page.tsx に、次のようなコードを書きます。
"use client";
import { useState } from "react";
export default function CounterPage() {
const [count, setCount] = useState(0);
function handleIncrement() {
setCount(count + 1);
}
return (
<main style={{ padding: "24px", fontFamily: "sans-serif" }}>
<h1>カウンター</h1>
<button
onClick={handleIncrement}
style={{ padding: "8px 16px", marginTop: "12px" }}
>
カウントアップ
</button>
</main>
);
}
TSXここではまだ「表示」は後でやるとして、useState とイベントだけに注目します。
const [count, setCount] = useState(0);
この 1 行がすごく重要です。
count … 「今のカウント値」を表す変数setCount … 「count を更新するための関数」useState(0) … 初期値を 0 にして state を作る
React に「このコンポーネントは count という状態を持ちます。最初は 0 です」と宣言しているイメージです。
そして handleIncrement の中で、
setCount(count + 1);
TSXと書くことで、「今の count に 1 を足した値を新しい状態として保存」しています。
ボタンには、
<button onClick={handleIncrement}>
TSXと書いてあるので、
ボタンがクリックされる
↓handleIncrement が呼ばれる
↓setCount(count + 1) が実行されて state が変わる
↓
React が自動でもう一度コンポーネントを描き直す
という流れになります。
必須課題② stateの値を画面に表示
「状態を持つ」だけでなく「表示」と結びつける
さっきのコードに、「今のカウント値」を表示する部分を足しましょう。
"use client";
import { useState } from "react";
export default function CounterPage() {
const [count, setCount] = useState(0);
function handleIncrement() {
setCount(count + 1);
}
return (
<main style={{ padding: "24px", fontFamily: "sans-serif" }}>
<h1>カウンター</h1>
<p style={{ fontSize: "20px", marginTop: "8px" }}>現在のカウント: {count}</p>
<button
onClick={handleIncrement}
style={{ padding: "8px 16px", marginTop: "12px" }}
>
カウントアップ
</button>
</main>
);
}
TSXここで重要なのは、
{count}
の部分です。
JSX の中で {} を使うと、「JavaScript の値」をそのまま表示できます。
つまり、
ブラウザで表示されるのは「現在のカウント: 0」からスタートし
ボタンを押すたびに count が 1, 2, 3… と変わり
表示も自動的に「現在のカウント: 1」「現在のカウント: 2」…と変わる
という動きになります。
ここで体感してほしいのは、
state(count)が変わる
↓
React が再レンダリングする
↓
JSX の {count} も新しい値になっている
という「状態 → UI」の流れです。
これが React の基本中の基本です。
挑戦課題:カウントダウン機能追加
「増やす」と「減らす」を両方持たせる
次は、カウントアップに加えてカウントダウンもできるようにしてみます。
ボタンを 2 つに増やして、それぞれ違うイベントハンドラをつなぎます。
"use client";
import { useState } from "react";
export default function CounterPage() {
const [count, setCount] = useState(0);
function handleIncrement() {
setCount(count + 1);
}
function handleDecrement() {
setCount(count - 1);
}
return (
<main style={{ padding: "24px", fontFamily: "sans-serif" }}>
<h1>カウンター</h1>
<p style={{ fontSize: "20px", marginTop: "8px" }}>現在のカウント: {count}</p>
<div style={{ marginTop: "12px", display: "flex", gap: "8px" }}>
<button
onClick={handleIncrement}
style={{ padding: "8px 16px" }}
>
+1
</button>
<button
onClick={handleDecrement}
style={{ padding: "8px 16px" }}
>
−1
</button>
</div>
</main>
);
}
TSX動きとしては、
+1 ボタンを押す → handleIncrement → setCount(count + 1)
−1 ボタンを押す → handleDecrement → setCount(count - 1)
となります。
「現在の値から更新する」パターンに慣れておく
もう一歩だけ大事なポイントを押さえておきます。
今は、
setCount(count + 1);
setCount(count - 1);
TSXと書いていますが、「前の値を使って新しい値を計算する」場合は、
次の書き方もよく使います。
setCount((prev) => prev + 1);
setCount((prev) => prev - 1);
TSXprev には「直前の count の値」が入ってきます。
この書き方は、連続で更新が起きるようなケースでも安全に動いてくれるため、
実務ではかなり多用されます。
最初のうちはどちらでも OK ですが、
「state 更新には“関数版”もある」と頭の片隅に置いておくと、後で楽になります。
まとめ:useStateでつかんでほしい感覚
この演習で本当に身につけてほしいのは、次の 3 点です。
useState は「現在の値」と「更新用の関数」をセットで提供してくれる([count, setCount])
state の値を JSX に {count} のように書くと、状態の変化に応じて表示も自動で変わる
ボタンの onClick で setCount を呼ぶことで、「ユーザー操作 → 状態更新 → UI 更新」の流れを作れる
ここが分かれば、カウンターだけでなく、
TODO の追加数
フォームに入力された文字数
いいねの数
など、あらゆる「変化する値」を扱えるようになっていきます。
もし余力があれば、次のようなアレンジも試してみてください。
0 未満にはならないように、count が 0 のときは −1 ボタンを無効にする
リセットボタンを追加して、count を 0 に戻せるようにする
