🖥️

Next.js入門7 Todoアプリ(タスク詳細)

に公開

記事一覧

  1. Next.js × Docker 最速環境構築
  2. Next.js入門1 ページ追加
  3. Next.js入門2 コンポーネント
  4. Next.js入門3 無記名関数 & イベントハンドラー
  5. Next.js入門4 Hooks
  6. Next.js入門5 Todoアプリ(タスク一覧)
  7. Next.js入門6 Todoアプリ(タスク追加)
  8. Next.js入門7 Todoアプリ(タスク詳細)
  9. Next.js入門8 Todoアプリ(タスク編集, 削除)

動的ルーティング

動的ルーティングとは

URLの一部が変化するパターンに応じて異なるページやコンポーネントを表示する仕組み

タスク詳細

動的ルーティング(http://localhost:3000/task/タスク番号)でアクセスできる詳細ページを作る

ページ作成

  1. task/[index]/page.tsxを作成
.
├── next-app
│   └── app
│       └── task
│           └── [index]
│               └── page.tsx # ここに作成
├── docker-compose.yml
└── Dockerfile
  1. task/[index]/page.tsxを記述
"use client";

// useは非同期処理を実行するためのHooks
import { use, useEffect, useState } from "react";
import { useRouter } from "next/navigation";
import { Button } from "@/components/button";
import { Task } from "@/types/task";

type Props = {
  // Promiseは非同期処理の結果を表す型
  params: Promise<{
    index: string; // urlの[index]
  }>;
};

function Page({ params }: Props) {
  // useを使用して非同期処理を実行し、結果を取得
  // parseIntで文字列を数値に変換
  const index = parseInt(use(params).index);
  const router = useRouter();
  const [task, setTask] = useState<Task>();
  const [tasks, setTasks] = useState<Task[]>([]);

  useEffect(() => {
    // ローカルストレージからタスクを取得
    const storedTasks = localStorage.getItem("tasks");

    // タスクがある場合はタスクを設定
    if (storedTasks) {
        const parsedTasks = JSON.parse(storedTasks);
        setTasks(parsedTasks); // 全体のタスクを設定
        setTask(parsedTasks[index]); // 選択したタスクを設定
    }
  }, [index]); // indexが変更されたら実行

  // タスクがない場合は何も表示しない
  if (!task) {
    return;
  }

  return (
    <div className="flex flex-col gap-8 items-center p-16">
      <h1 className="text-4xl font-bold">タスク詳細</h1>

      <div className="w-full flex flex-col gap-8 border rounded-2xl p-8">
        <div className="flex gap-2 border-b">
          <label className="font-bold">タスク名</label>
          <p>{task.title}</p>
        </div>

        <div className="flex flex-col gap-2">
          <label className="font-bold border-b">内容</label>
          <p>{task.content}</p>
        </div>
      </div>

      <div className="w-full flex justify-between">
        <Button
          onClick={() => {
            router.push("/task"); // タスク一覧に戻る処理
          }}
        >
          戻る
        </Button>
      </div>
    </div>
  );
}

export default Page;

一覧ページ編集

  1. components/table.tsxを編集
// useRouterをインポート
import { useRouter } from "next/navigation";
// Buttonコンポーネントをインポート
import { Button } from "@/components/button";
import { Task } from "@/types/task";

type Props = {
  tasks: Task[];
};

export function Table({ tasks }: Props) {
  // ルーターを取得
  const router = useRouter();

  return (
    <table className="w-full [&_td]:p-2 [&_td]:border">
      <thead>
        <tr>
          <td>タイトル</td>
          <td className="w-2/3">内容</td>
          {/* ボタンのための列を追加 */}
          <td className="w-20"></td>
        </tr>
      </thead>
      <tbody>
        {/* 渡されたtasksを回してテーブルを列を作成する */}
        {tasks.map((task, index) => (
          <tr key={index}>
            <td>{task.title}</td>
            <td>{task.content}</td>
            <td>
              <Button
                onClick={() => {
                  // 詳細ページに遷移する処理
                  router.push(`/task/${index}`);
                }}
              >
                詳細
              </Button>
            </td>
          </tr>
        ))}
      </tbody>
    </table>
  );
}

http://localhost:3000/taskから詳細ページにアクセスし、以下の画像のようになっていればOK

  • 一覧ページ

  • 詳細ページ

Discussion