クエリパラメータを使った一覧画面の開発 ～ Next.js、React、shadcn/ui を活用して

この記事では、Next.js と React、そして shadcn/ui を利用して、URL のクエリパラメータを活用した一覧画面の開発方法についてご紹介します。
一覧画面では、たとえば「カテゴリー」や「ソート順」、「検索キーワード」などのクエリパラメータを利用して、ユーザーに対して柔軟かつダイナミックなフィルタリングやソート機能を提供することができます。

## 1. はじめに

近年の Web アプリケーションでは、一覧画面で表示するデータをクエリパラメータでコントロールするケースが増えています。
たとえば、
•/products?category=shoes&sort=price_asc
のような URL を使うことで、特定のカテゴリーに絞り込んだり、価格の昇順で並べ替えたりすることが可能になります。

Next.js のルーティング機能と React の状態管理を組み合わせれば、こうした動的な一覧画面が比較的シンプルに実装できます。さらに、shadcn/ui を利用することで、洗練された UI コンポーネントを手軽に利用でき、ユーザーにとって見やすい画面を作ることができます。

## 2. Next.js でのクエリパラメータの扱い

Next.js では、App Router（または従来の pages ディレクトリ）を使って、クエリパラメータを簡単に取得できます。
例えば、Next.js 13 以降の App Router を利用している場合は、ページコンポーネント内で useSearchParams フックを使うことで、URL のクエリパラメータにアクセスできます。
※useSearchParamsはサーバ コンポーネントでは利用できません。

```typescript
// app/products/page.tsx (Next.js 13 App Router の例)
"use client";

import { useSearchParams } from "next/navigation";
import { useEffect, useState } from "react";

export default function ProductsPage() {
  const searchParams = useSearchParams();
  const category = searchParams.get("category") || "all";
  const sort = searchParams.get("sort") || "default";
  const [products, setProducts] = useState([]);

  useEffect(() => {
    // ここでは、クエリパラメータに応じた API 呼び出し例を示します。
    async function fetchProducts() {
      const res = await fetch(`/api/products?category=${category}&sort=${sort}`);
      const data = await res.json();
      setProducts(data);
    }
    fetchProducts();
  }, [category, sort]);

  return (
    <div>
      <h1>製品一覧（カテゴリー: {category} / 並び順: {sort}）</h1>
      {/* 製品データを一覧表示するコンポーネントを後述の shadcn/ui で作成 */}
      <ProductsList products={products} />
    </div>
  );
}
```

## 3. shadcn/ui を使った一覧画面の実装

shadcn/ui は、デザインにこだわったコンポーネント群を提供してくれるため、すぐに洗練された UI を実現できます。
ここでは、一覧表示用に簡単なテーブルやカード形式のコンポーネントを使って製品一覧を表示する例を紹介します。

### 3.1 製品一覧表示用のコンポーネント

以下は、shadcn/ui のスタイルが反映されたシンプルな一覧表示コンポーネントの例です。
実際には、shadcn/ui の公式コンポーネント（Button、Card、Table など）をインポートして使いますが、ここではわかりやすさのためにシンプルな例としています。
```typescript
// components/ProductsList.tsx
import React from "react";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";

type Product = {
  id: number;
  name: string;
  price: number;
  category: string;
};

type ProductsListProps = {
  products: Product[];
};

export default function ProductsList({ products }: ProductsListProps) {
  if (products.length === 0) {
    return <p>製品が見つかりませんでした。</p>;
  }

  return (
    <Card>
      <CardHeader>
        <CardTitle>製品一覧</CardTitle>
      </CardHeader>
      <CardContent>
        <Table>
          <TableHeader>
            <TableRow>
              <TableHead>ID</TableHead>
              <TableHead>製品名</TableHead>
              <TableHead>価格</TableHead>
              <TableHead>カテゴリー</TableHead>
            </TableRow>
          </TableHeader>
          <TableBody>
            {products.map((product) => (
              <TableRow key={product.id}>
                <TableCell>{product.id}</TableCell>
                <TableCell>{product.name}</TableCell>
                <TableCell>{product.price}円</TableCell>
                <TableCell>{product.category}</TableCell>
              </TableRow>
            ))}
          </TableBody>
        </Table>
      </CardContent>
    </Card>
  );
}
```
この例では、shadcn/ui の Card や Table コンポーネントを利用して、製品一覧をスタイリッシュに表示しています。

## 4. クエリパラメータと UI の連動

一覧画面において、ユーザーがクエリパラメータを変更する（例えば、カテゴリーを選んだり、ソート順を変更する）場合、その状態に応じた API 呼び出しと UI 更新が必要になります。
たとえば、検索バーやフィルターコンポーネントを作成し、ユーザーの選択に応じて URL のクエリパラメータを更新することで、一覧画面全体が連動して更新されるような実装が考えられます。

### 4.1 フィルターコンポーネントの例
```typescript
// components/ProductsFilter.tsx
"use client";

import { useRouter, useSearchParams } from "next/navigation";
import { Button } from "@/components/ui/button";

export default function ProductsFilter() {
  const router = useRouter();
  const searchParams = useSearchParams();
  const currentCategory = searchParams.get("category") || "all";

  const handleFilterChange = (category: string) => {
    // クエリパラメータを更新して、一覧画面に反映する
    const newParams = new URLSearchParams(searchParams.toString());
    newParams.set("category", category);
    router.push(`/products?${newParams.toString()}`);
  };

  return (
    <div>
      <Button variant={currentCategory === "all" ? "default" : "outline"} onClick={() => handleFilterChange("all")}>
        全て
      </Button>
      <Button variant={currentCategory === "shoes" ? "default" : "outline"} onClick={() => handleFilterChange("shoes")}>
        靴
      </Button>
      <Button variant={currentCategory === "bags" ? "default" : "outline"} onClick={() => handleFilterChange("bags")}>
        バッグ
      </Button>
      {/* 他のフィルターも追加可能 */}
    </div>
  );
}
```
この例では、shadcn/ui の Button コンポーネントを使って、カテゴリーのフィルタを実装しています。
ユーザーがボタンをクリックすると、router.push を利用して URL のクエリパラメータが更新され、一覧画面が再レンダリングされます。

## 5. まとめ
•クエリパラメータで柔軟な一覧画面を実現
URL のクエリパラメータを使って、フィルタリングやソート、ページネーションなどの動的な機能を実装することで、ユーザーに直感的な操作体験を提供できます。
•Next.js と React の連携
Next.js の useSearchParams や useRouter を使うことで、クエリパラメータの取得・更新がシンプルに行えます。
•shadcn/ui を利用した洗練された UI
shadcn/ui のコンポーネントを活用することで、見た目にもこだわった一覧画面やフィルター、ボタンなどを手軽に実装可能です。

このように、Next.js、React、そして shadcn/ui を組み合わせれば、クエリパラメータをうまく活用したダイナミックな一覧画面が比較的簡単に作成できます。ぜひ、実際のプロジェクトで試してみて、ユーザーに優しいインターフェースの構築を目指してください。