Next.jsでnuqsを試す(+サーバーコンポーネント)
URLクエリパラーメータをタイプセーフに扱えるnuqsを試してみた。
最新のNext.js(v15)ではnuqsはインストール出来なかった。React.jsの依存が解決できない。nuqs自体は2.0.4をインストールした。
app-router
と page-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