Next.js(App Router)のuseSearchParams()の値をPage Propsとおなじフォーマットに変換する
Next.js(App Router)ではServer ComponentsとClient Componentsでルーティング情報が取得方法が微妙にことなります。
Server Componentsの場合
Server ComponentsではPage ComponentのPropsとしてparams
やsearchParams
などのルーティング情報が取得できます。
Next15以降では以下のようなページを作成して
type Props = {
params: Promise<{ [key: string]: string }>
searchParams: Promise<{ [key: string]: string | string[] | undefined }>
}
export default async function Page(props: Props) {
const params = await props.params
const searchParams = await props.searchParams
return (
<div>
<p>params : {params.slug}</p>
<p>searchParams : {searchParams.query}</p>
</div>
);
}
http://localhost:3000/foo/bar?query=abc
などのURLにアクセスするとブラウザには以下のように表示されます。
params : [foo, bar]
searchParams : abc
※ Next14ではparams、searchParamsはPromiseではないので注意してください。
Client Componentの場合
Client ComponentではuseParams()
やuseSearchParams()
を利用してルーティング情報が取得できます。
ただし、useSearchParams()
で取得できるオブジェクトはURLSearchParamsなのでPage ComponentのPropsと形を合わせるには少し調整が必要です。
entries()
を利用してイテレーターとして取り出し、Object.fromEntries()
を利用してオブジェクトに変換することで同じような値を取得できます。
Object.fromEntries(useSearchParams().entries())
つまり以下のようにするとPage ComponentのPropsと同じような値になります。
export default function ClinentComponent() {
const params = useParams();
const searchParams = Object.fromEntries(useSearchParams().entries())
return (
<div>
<p>params : {params.slug}</p>
<p>searchParams : {searchParams.query}</p>
</div>
);
}
重複するquery文字列に対応
http://localhost:3000/foo/bar?query=abc&query=123
などの重複するquery文字列がある場合に上記のコードでは共通になりません。
以下のようになりClient Componentでは重複するquery文字列が省略されてしまいます。
params : [foo, bar]
searchParams : [abc, 123]
params : [foo, bar]
searchParams : 123
これを防ぐにはentries()
で取得せずにforEach()
で取得してgetAll()
で複数のquery文字列があるかどうかをチェックしながらsearchParams
を組み立てていきます。
export default function ClinentComponent() {
const params = useParams();
const searchParams: { [key: string]: string[] | string } = {};
const searchParamsObject = useSearchParams();
searchParamsObject.forEach((_, key) => {
const values = searchParamsObject.getAll(key);
searchParams[key] = values.length > 1 ? values : values[0];
});
return (
<div>
<p>params : {params.slug}</p>
<p>searchParams : {searchParams.query}</p>
</div>
);
}
このようにすることでNext.js(App Router)でServer ComponentsとClient Componentsでルーティング情報を同じ型で取り扱うことができるようになります。
Discussion