👐

Next.jsでnuqsを試す(+サーバーコンポーネント)

2024/10/25に公開

URLクエリパラーメータをタイプセーフに扱えるnuqsを試してみた。

https://nuqs.47ng.com/

最新のNext.js(v15)ではnuqsはインストール出来なかった。React.jsの依存が解決できない。nuqs自体は2.0.4をインストールした。

https://nuqs.47ng.com/docs/adapters#nextjs-app-router

app-routerpage-router で導入方法が違うらしい。今回はapp-router前提で進める。

アプリケーション全体をNuxtAdapterでラップする。 特にuse clientを意識する必要はないらしい

import type { Metadata } from "next";
import localFont from "next/font/local";
import "./globals.css";

import { NuqsAdapter } from "nuqs/adapters/next/app";

export const metadata: Metadata = {
  title: "Create Next App",
  description: "Generated by create next app",
};

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
      <body>
        <NuqsAdapter>{children}</NuqsAdapter>
      </body>
    </html>
  );
}

クライアントコンポーネントから利用する場合は次の用に使う。

"use client";

import { useQueryState } from "nuqs";

export default function Home() {
  const [name, setName] = useQueryState("name");

  return (
    <>
      <div>
        {" "}
        <input value={name || ""} onChange={(e) => setName(e.target.value)} />
        <button type="button" onClick={() => setName(null)}>
          クリア
        </button>
      </div>{" "}
      <p>{name || "名無しさん"}こんにちは</p>
    </>
  );
}

おもしろことにsetter経由で値をかえると、クエリパラメータが動的に変化する。フィルター機能を実装する場合に使えるのではないだろうか。フィルタの条件がはURLで表現されると、ユーザー間での共有ができたりよく使う条件をブックマークしたりと活用範囲が地味に広いだろう。

パーサーとデフォルト値

パーサーを使えばURLから取得した値を数値等として扱うこともできる。

const [count, setCount] = useQueryState(  
    "count",  
    parseAsInteger.withDefault(0),  
);

withDefaultは名前の通りパラメーターが指定されなかった場合のデフォルト値を指定できる。この値がURLに設定されることはない。ちなみに、数値として解釈できない値をパラメーターに指定した場合はデフォルト値が使われるようだ。

パーサーは自前で実装することもできる。

サーバーコンポーネント

サーバーコンポーネントにも対応している。ネストしたコンポーネントからパラメータを参照する場合はパラメータへのアクセスを関数として切り出すことを推奨しているようだ。

import { createSearchParamsCache, parseAsString } from "nuqs/server";

export const searchParam = {
  name: parseAsString.withDefault("腹筋"),
};

export const serachParamsCache = createSearchParamsCache(searchParam);
import type { SearchParams } from "nuqs/server";
import { serachParamsCache } from "./searchParams";

type PageProps = {
  searchParams: SearchParams;
};

export default function Home({ searchParams }: PageProps) {
  const { name } = serachParamsCache.parse(searchParams);
  return (
    <>
      <p>{name || "名無しさん"}こんにちは</p>
    </>
  );
}

createSearchParamsCacheのcacheはreactのcacheを使っているらしい。パラメータの定義(サンプルコードではsearchParam)はクライアントコンポーネントから扱うこともできる。

"use client";

import { useQueryStates } from "nuqs";
import { searchParam } from "./searchParams";

export function Child() {
  const [{ name }, setParams] = useQueryStates(searchParam);
  return (
    <>
      <p>{name}</p>
      <button
        type="button"
        onClick={() => {
          setParams({ name: "John" });
        }}
      >
        変更
      </button>
    </>
  );
}

ただし、setterを使ってパラメータを更新してもサーバーコンポーネントは更新されない。

Discussion