📝

【React修行日記】動的ルーティングとパラメータ型

に公開

学習の目的

  • React Router の useParams() フックを理解して使えるようにする
  • TypeScript で useParams() の型を安全に扱う
  • 一覧ページと詳細ページの動的ルーティングを実装できるようになる

React Router のルーティングと動的ルートの仕組み

ルート(=URLのパス)には大きく2つのタイプがある

種類 意味
静的ルート /about, /contact 固定ページ。誰が開いても同じ内容を表示する。
動的ルート(Dynamic Route) /users/:id, /products/:productId パスの一部が変数のように変わるページ。

動的ルーティングの仕組み

React Routerでは、動的ルートは次のように設定する。

<Route path="/users/:id" element={<UserDetail />} />

:idの部分が動的パラメータとして扱われ、例えば/users/1/users/2は同じコンポーネント(UserDetail)にアクセスするが、URLのidの値によって表示内容が切り替わる。

useParams()の概念

useParams()は、URLに含まれるパラメータを取得するReact Routerのフック
URLパラメータはuseParams()で取得できる。

const { id } = useParams();

たとえばURLが/users/3のとき、id"3"という文字列として取得される。

なぜ動的ルートが必要か?

動的ルーティングは、アプリで「ページの中身がデータ依存で変わる」個別ページを持つときに必要となる。
例:

機能 URL例 備考
ユーザプロフィール /users/123 IDごとに内容が変わる
商品詳細 /products/999 同じテンプレートで別データを表示
記事詳細 /articles/react-router-basics スラッグやIDで記事を特定

ユーザー一覧とユーザー詳細の実装

useParams()を使って、ユーザー一覧から動的に詳細ページへ遷移し、それぞれのユーザー情報を表示する仕組みを実装してみた...!

ダミーデータの準備

// src/data/users.ts
export type User = {
  id: number;
  name: string;
  age: number;
};

export const users: User[] = [
  { id: 1, name: "Taro", age: 25 },
  { id: 2, name: "Hanako", age: 28 },
  { id: 3, name: "Jiro", age: 22 },
];

ルーティング設定

// src/App.tsx
import { Routes, Route } from "react-router-dom";
import UserList from "@/pages/UserList";
import UserDetail from "@/pages/UserDetail";

export default function App() {
  return (
    <Routes>
      <Route path="/users" element={<UserList />} />
      <Route path="/users/:id" element={<UserDetail />} />
      <Route path="*" element={<h2>ページが見つかりません。</h2>} />
    </Routes>
  );
}

ポイント

  • /users/users/:idの関係
    /usersはユーザー一覧ページ。
    /users/:idは動的ルート(ユーザー詳細ページ)で、:idがURLパラメータとして扱われる。
    →同じ「/users」階層でも、:idの有無で別ページにルーティングされる。
  • *(ワイルドカードルート)
    どのルートにも一致しなかった場合に表示する「404ページ」。

ユーザー一覧ページ

import { users } from "@/data/users";
import { Link } from "react-router-dom";

export default function UserList() {
  return (
    <div>
      <h2 className="text-2xl font-bold mb-6">ユーザー一覧</h2>

      <ul className="space-y-3">
        {users.map((user) => (
          <li key={user.id}>
            <Link
              to={`/users/${user.id}`}
              className="text-blue-600 hover:underline hover:text-blue-800"
            >
              {user.name}
            </Link>
          </li>
        ))}
      </ul>
    </div>
  );
}

ユーザー詳細ページ

import { users } from "@/data/users";
import { Link, useParams } from "react-router-dom";

export default function UserDetail() {
  // URLパラメータを取得
  const { id } = useParams<{ id: string }>();

  // IDに一致するユーザを検索
  const user = users.find((u) => u.id === Number(id));

  if (!user) {
    return (
      <div>
        <h2>ユーザが見つかりません。</h2>
        <Link to="/users">← 戻る</Link>
      </div>
    );
  }

  return (
    <div className="flex flex-col items-center gap-6">
      <h2 className="text-2xl font-bold">{user.name} のプロフィール</h2>

      <div className="border-1 border-gray-600 p-2">
        <p>ID:{user.id}</p>
        <p>年齢:{user.age}</p>
      </div>
      <Link
        to="/users"
        className="text-blue-600 hover:underline hover:text-blue-800"
      >
        ← 一覧に戻る
      </Link>
    </div>
  );
}

ポイント

// URLパラメータを取得
const { id } = useParams<{ id: string }>();
// IDに一致するユーザを検索
const user = users.find((u) => u.id === Number(id));
  • useParams<{ id: string }>()
    URLの/users/:idの「:id」部分を取得する。
    TypeScriptの型パラメータ{ id: string }を指定することで、idが必ず存在し、文字列(string)として扱えるようになる。
    (デフォルトではstring | undefinedなので、型を明示することで型安全にできる)

  • Number(id)
    useParams()で取得した値は 常に文字列(string) なので、usersのid(number型)と比較するために数値へ変換している。

まとめ

  • URLの一部に:idのような動的パラメータを設定することで、同じコンポーネントで複数のページを表示できる
  • useParams()を使って、URLからパラメータを取得できる
  • TypeScriptで型パラメータを指定することで、型安全にパラメータを扱える
  • この知識を使えば、一覧ページから詳細ページへの動的な遷移を安全に実装できる

参考

https://reactrouter.com/api/hooks/useParams

Discussion