第3部:実践チュートリアル Step 15 / 20

Todoアプリ - フロントエンド編

Next.jsを使って、Todoアプリの画面を作成します。まずはモックデータで動く状態を作り、次のページでAPIと連携させます。

完成イメージ

Todoリスト

  • 買い物に行く
  • 部屋を掃除する

Step 1: Todoの型定義を作成

AIへの指示

frontend/src/types/todo.ts を作成して、
Todoの型定義を追加してください。

Todo型:
- id: number
- title: string
- completed: boolean

Step 2: Todoリストコンポーネントを作成

AIへの指示

frontend/src/app/page.tsx を編集して、
Todoリストアプリを作成してください。

機能:
1. Todoの一覧表示
2. 新しいTodoの追加フォーム
3. Todoの完了/未完了の切り替え(チェックボックス)
4. Todoの削除ボタン
5. 完了したTodoは取り消し線を表示

まずはモックデータで動くようにしてください。
APIとの連携は後で行います。

使用技術:
- useState で状態管理
- Tailwind CSS でスタイリング

完成コード例

src/types/todo.ts

export interface Todo {
  id: number;
  title: string;
  completed: boolean;
}

src/app/page.tsx

"use client";

import { useState } from "react";
import { Todo } from "@/types/todo";

// モックデータ
const initialTodos: Todo[] = [
  { id: 1, title: "買い物に行く", completed: false },
  { id: 2, title: "部屋を掃除する", completed: true },
];

export default function Home() {
  const [todos, setTodos] = useState<Todo[]>(initialTodos);
  const [newTitle, setNewTitle] = useState("");

  // Todo追加
  const addTodo = () => {
    if (!newTitle.trim()) return;
    const newTodo: Todo = {
      id: Date.now(),
      title: newTitle,
      completed: false,
    };
    setTodos([...todos, newTodo]);
    setNewTitle("");
  };

  // 完了切り替え
  const toggleTodo = (id: number) => {
    setTodos(todos.map(todo =>
      todo.id === id ? { ...todo, completed: !todo.completed } : todo
    ));
  };

  // 削除
  const deleteTodo = (id: number) => {
    setTodos(todos.filter(todo => todo.id !== id));
  };

  return (
    <main className="min-h-screen p-8 max-w-md mx-auto">
      <h1 className="text-2xl font-bold mb-6 text-center">
        Todoリスト
      </h1>

      {/* 追加フォーム */}
      <div className="flex mb-6">
        <input
          type="text"
          value={newTitle}
          onChange={(e) => setNewTitle(e.target.value)}
          onKeyDown={(e) => e.key === "Enter" && addTodo()}
          placeholder="新しいTodoを入力..."
          className="flex-1 border rounded-l-lg px-4 py-2"
        />
        <button
          onClick={addTodo}
          className="bg-blue-500 text-white px-4 py-2
            rounded-r-lg hover:bg-blue-600"
        >
          追加
        </button>
      </div>

      {/* Todoリスト */}
      <ul className="space-y-2">
        {todos.map((todo) => (
          <li
            key={todo.id}
            className="flex items-center justify-between
              p-3 bg-gray-50 rounded"
          >
            <div className="flex items-center">
              <input
                type="checkbox"
                checked={todo.completed}
                onChange={() => toggleTodo(todo.id)}
                className="mr-3"
              />
              <span
                className={todo.completed ?
                  "line-through text-gray-400" : ""}
              >
                {todo.title}
              </span>
            </div>
            <button
              onClick={() => deleteTodo(todo.id)}
              className="text-red-500 hover:text-red-700"
            >
              削除
            </button>
          </li>
        ))}
      </ul>
    </main>
  );
}

Step 3: 動作確認

  1. フロントエンドサーバーを起動: npm run dev
  2. ブラウザで http://localhost:3000 を開く
  3. 以下を確認:
    • Todoが一覧表示されている
    • 新しいTodoを追加できる
    • チェックボックスで完了/未完了を切り替えられる
    • 削除ボタンで削除できる

注意:この時点ではデータはメモリ上にあるため、ページをリロードすると初期状態に戻ります。次のページでAPIと連携して永続化します。

前へ:バックエンド編 次へ:連携編