State問題:カウンター
「+1」「-1」「リセット」ボタンを作り、0未満にならないカウンターを実装してください。答えでは関数形式のstate更新を使います。
解答例と実行結果
解答例JSX
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
return (<>
<p>{count}</p>
<button onClick={() => setCount(c => Math.max(0, c - 1))}>-1</button>
<button onClick={() => setCount(c => c + 1)}>+1</button>
<button onClick={() => setCount(0)}>リセット</button>
</>);
}操作結果OUTPUT
初期: 0 → +1 → 1 → -1 → 0フォーム問題:Todo
入力した内容を追加し、完了ボタンで取り消し線を切り替え、削除ボタンで消してください。空入力は追加しません。
解答例と実行結果
解答例JSX
function addTodo(event) {
event.preventDefault();
const value = text.trim();
if (value === '') return;
setTodos(current => [...current,
{ id: crypto.randomUUID(), text: value, done: false }]);
setText('');
}
function toggle(id) {
setTodos(current => current.map(todo =>
todo.id === id ? { ...todo, done: !todo.done } : todo));
}操作結果OUTPUT
Reactを学ぶ → 追加
Reactを学ぶ → 完了
表示: Reactを学ぶ(取り消し線)Effect問題:通信状態
API通信を「読み込み中」「成功」「失敗」の3状態で表示してください。Effectのcleanupで通信を中断できるようにします。
解答例と確認ポイント
解答例JSX
import { useEffect, useState } from 'react';
useEffect(() => {
const controller = new AbortController();
setStatus('loading');
fetch('/api/items', { signal: controller.signal })
.then(response => {
if (response.ok === false) throw new Error('HTTPエラー');
return response.json();
})
.then(items => { setItems(items); setStatus('success'); })
.catch(error => { if (error.name !== 'AbortError') setStatus('error'); });
return () => controller.abort();
}, []);確認例:開始直後は「読み込み中」、成功時は一覧、サーバー停止時は「取得できませんでした」を表示します。
Reducer問題:買い物かご
商品追加、数量変更、全削除の3つのactionを持つReducerを作ってください。合計金額はstateへ重複保存せず、itemsから計算します。
解答例と実行結果
ReducerJSX
function cartReducer(state, action) {
switch (action.type) {
case 'add': return [...state, action.item];
case 'clear': return [];
case 'remove': return state.filter(item => item.id !== action.id);
default: return state;
}
}
const total = items.reduce((sum, item) => sum + item.price, 0);実行結果OUTPUT
ノート 300円を追加 → 合計: 300円
ペン 120円を追加 → 合計: 420円
全削除 → 合計: 0円発展課題:読書記録
タイトル、著者、読了状態を持つ読書記録アプリを作りましょう。登録・検索・完了切り替え・削除・localStorage保存を実装し、次の点を確認します。
- フォーム入力はcontrolled componentになっているか
- リストのkeyに固有IDを使っているか
- 検索結果をEffectではなく計算値として作っているか
- localStorageの読み書きをEffectで同期しているか
- 空データ、長いタイトル、保存失敗を考慮しているか
実装できたら、TypeScriptでデータ型を付け、Next.jsのページへ移してみましょう。コードを完成させることだけでなく、stateの所有者とデータの流れを説明できることが目標です。
