クエリパラメータを取得するときの注意点(Next.js編)

クエリパラメータを取得するときの注意点(Next.js編)

投稿日: 2024年12月23日

Tips
要約
  • Next.jsのCSRコンポーネントでuseSearchParamsを使用すると、productionビルド時にMissing Suspense boundaryエラーが発生することがある。
  • この問題を解決するために、同じディレクトリ内にlayout.tsxを作成し、Suspenseでchildrenをラップするエレガントな方法が推奨される。
  • 定期的にnpm run buildを実行する習慣をつけることで、ローカル環境と本番環境の整合性を保つことが重要。

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 だったときは、定数 rawReturnPathnullが格納されます。あと、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)が失敗する」という事態のダメージが大きくなりますので・・・。

シェア!

Threads
Loading...
記事一覧に戻る
Threads
0