クエリパラメータを取得するときの注意点(Next.js編)
投稿日: 2024年12月23日
Next.js のCSRコンポーネントで、クエリパラメータの取得に useSearchParams
を利用しようとしてハマった🤮ので、その問題と解決法を共有しておきます。
URL(URI)のなかで ?
以降の部分を「クエリパラメータ」や「クエリ文字列」といいます。例えば http://localhost:3000/login?returnPath=/admin/posts/new
というURLのうち、returnPath=/admin/posts/new
の部分がクエリパラメータとなります。
このクエリパラメータを関数コンポーネントのなかで取得・参照したいときに、useSearchParams
というフックが利用できます。
具体的には、次のようなコードで /login?returnPath=/admin/posts/new
というパスにアクセスがあったとき、/admin/posts/new
という文字列の取得ができます。
"use client";
import { useSearchParams } from "next/navigation";
const Page: React.FC = () => {
const searchParams = useSearchParams();
const rawReturnPath: string | null = searchParams.get("returnPath");
return (
<main>
<div>rawReturnPath={rawReturnPath}</div>
</main>
);
};
export default Page;
ちなみにパスが /login
や /login?name=hogehoge
だったときは、定数 rawReturnPath
にnull
が格納されます。あと、URLにはhttp://localhost:3000/login#hoge
のように #
につづく文字列がありますが、こちらはuseSearchParams
では取得できないので注意ください(詳しくは、茜さんが書かれた「【supabase】Google認証の実装方法」をご覧ください)。
さて、上記のようなコードを書いてnpm run dev
で開発モードでサーバを起動して、http://localhost:3000/login?returnPath=/admin/posts/new
にアクセスすると、確かに意図するように動作します。はい、警告もエラー発生せずに問題なく動作します😀。
しかし、本番環境に向けてnpm run build
を実行すると、次のようなエラーが発生してしまいます😇😇😇
Creating an optimized production build ...
✓ Compiled successfully
✓ Linting and checking validity of types
✓ Collecting page data
Generating static pages (0/XX) [= ] ⨯ useSearchParams() should be wrapped in a suspense boundary at page "/login". Read more: https://nextjs.org/docs/messages/missing-suspense-with-csr-bailout
Error occurred prerendering page "/login". Read more: https://nextjs.org/docs/messages/prerender-error
これは Missing Suspense boundary with useSearchParams という問題のようです。詳しくは https://nextjs.org/docs/messages/missing-suspense-with-csr-bailout に書いてあります(僕は半分も理解できなかった・・・)。
この問題に対しては、いくつかの対処法があるようですが、ぶべさん🐈⬛が提案してくださった方法が、エレガント😎に問題を解決できたので共有しておきます。
いま、この問題が src/app/login/page.tsx
で起きているとします。その前提で src/app/login/layout.tsx
を作成して、次のようにコードを記述します。
"use client";
import React from "react";
import { Suspense } from "react";
interface Props {
children: React.ReactNode;
}
const Layout: React.FC<Props> = (props) => {
const { children } = props;
return <Suspense>{children}</Suspense>;
};
export default Layout;
これでnpm run build
で発生していたエラーがきれいに解決します。公式ページで紹介している方法はpage.tsx
のコードを変更する必要があるのですが、この方法ではpage.tsx
をそのままに問題を解決できるのが素敵🐈⬛です。
オリジナルアプリ制作(ポートフォリオ制作)のフェーズに入ったら、定期的にnpm run build
を実行する習慣をつけることをお勧めします。最終的には Vercel などを使ってウェブにオリアプを公開することになると思いますが、その際、必ずnpm run build
的な処理が必要になります。
開発が進めば進むほど「ローカル環境(npm run dev
)では問題なく動くのに、Vercelへのデプロイ(npm run build
)が失敗する」という事態のダメージが大きくなりますので・・・。