AppRouterでSearchParamsを変更した際にLoadingが発生しない問題について
本文の状態
日本語全文を表示中
詳細モードで約3分の本文を読めます。
同じ出来事の情報源
この情報源を基点に整理
CoeFont Tech Blog
AppRouterでSearchParamsを変更してもLoadingが走らない問題を解説。サンプルリポジトリとPRで解決策を示す。
Continue in AI NEW LAB
このニュースを、実務の判断につなげる
AI NEW LABで、試したことや先に確認したい条件を共有できます。まずはログインなしで読めます。
AI NEW LABで論点を見るSource Article
元記事を日本語で読む
本文に関係しない購読案内、埋め込み通知、サイト内プロモーションは除いています。
Next.js
React tech株式会社CoeFontでプロダクト開発をしている uzimaru です。 AppRouter で SearchParams の変更をしたときに Loading が走らないという状況に遭遇したので記事にしようと思います。
また、今回の事例のサンプルのリポジトリは ↓ になっています https://github.com/uzimaru0000/example-change-qs-for-approuter
変更後については こちら の PR を参照してみてください
変更前 https://example-change-qs-for-approuter-uzimaru0000.vercel.app/search
変更後 https://example-change-qs-for-approuter-git-fix-loading-uzimaru0000.vercel.app/search
検索画面のようなページで検索条件を SearchParams に反映させ URL としてページを表現することはよくある実装だと思います。 その際、条件の変更に応じて検索画面にはローディング中の表示をしたくなります。

AppRouter では loading.tsx

issue を調べてみるとどうやら同じ問題に遭遇している人はいくらか居るようでした。
https://github.com/vercel/next.js/issues/49297 https://github.com/vercel/next.js/issues/58179
issue が open になっているところからもまだ対応はされていないようです。 そこで issue の中に workaround を共有してくれている人が居たので解決がすることが出来ました。
自分で Suspense を設定する
import { getSearchProducts } from "@/usecase/product/server"; import { Product } from "./_components/Product"; import { Filter } from "@/usecase/product"; type Props = { searchParams: Filter } const Page: React.FC = async ({ searchParams }) => { const result = await getSearchProducts(searchParams); return ( {result.map((x) => { return ; })}
); }; export default Page;
このファイルを以下のように変更します。
import { getSearchProducts } from "@/usecase/product/server"; import { Product } from "./_components/Product"; import { Filter } from "@/usecase/product"; +import { Suspense } from "react"; +import Loading from "./loading"; type Props = { searchParams: Filter; }; const Page: React.FC = async ({ searchParams }) => { + return ( + }> + + + ); +}; + +const Wrapper: React.FC = async ({ searchParams }) => { const result = await getSearchProducts(searchParams); return (
- 非同期なコンポーネントを分離する
- Suspense に key をつける
Suspense に key をつけることで、一度 suspend したものとは別のコンポーネントになるため再度 Suspense されます。(ここあたりの認識が曖昧なので間違っていたらコメント下さい🙇) 今回は searchParams
この 2 点の修正を加えることで SearchParams を変更しても Loading を走らせることが出来ます!

注意点として、layout.tsx
workaround でしたが上記の 2 点の修正をすることで SearchParams を変更しても Loading を走らせることが出来ました。 詰まったときは 「なんで URL が変わってるのに Loading されないんだ🤔」と思いましたが、ドキュメントにも書いている通り
An instant loading state is fallback UI that is shown immediately upon navigation.
なのでナビゲーション直後の loading のみを扱うためのようです。(とは言え URL は変更されてるからナビゲーション直後な気もする...) 今回の workaround として変更を入れている部分は Streaming with Suspense で対応する場所なのかもしれません。
CoeFontPublicationAI音声プラットフォーム「CoeFont(コエフォント)」の公式テックブログです。

原文を表示
Next.js
React tech株式会社CoeFontでプロダクト開発をしている uzimaru です。 AppRouter で SearchParams の変更をしたときに Loading が走らないという状況に遭遇したので記事にしようと思います。
また、今回の事例のサンプルのリポジトリは ↓ になっています https://github.com/uzimaru0000/example-change-qs-for-approuter
変更後については こちら の PR を参照してみてください
変更前 https://example-change-qs-for-approuter-uzimaru0000.vercel.app/search
変更後 https://example-change-qs-for-approuter-git-fix-loading-uzimaru0000.vercel.app/search
検索画面のようなページで検索条件を SearchParams に反映させ URL としてページを表現することはよくある実装だと思います。 その際、条件の変更に応じて検索画面にはローディング中の表示をしたくなります。

AppRouter では loading.tsx

issue を調べてみるとどうやら同じ問題に遭遇している人はいくらか居るようでした。
https://github.com/vercel/next.js/issues/49297 https://github.com/vercel/next.js/issues/58179
issue が open になっているところからもまだ対応はされていないようです。 そこで issue の中に workaround を共有してくれている人が居たので解決がすることが出来ました。
自分で Suspense を設定する
import { getSearchProducts } from "@/usecase/product/server"; import { Product } from "./_components/Product"; import { Filter } from "@/usecase/product"; type Props = { searchParams: Filter } const Page: React.FC = async ({ searchParams }) => { const result = await getSearchProducts(searchParams); return ( {result.map((x) => { return ; })}
); }; export default Page;
このファイルを以下のように変更します。
import { getSearchProducts } from "@/usecase/product/server"; import { Product } from "./_components/Product"; import { Filter } from "@/usecase/product"; +import { Suspense } from "react"; +import Loading from "./loading"; type Props = { searchParams: Filter; }; const Page: React.FC = async ({ searchParams }) => { + return ( + }> + + + ); +}; + +const Wrapper: React.FC = async ({ searchParams }) => { const result = await getSearchProducts(searchParams); return (
- 非同期なコンポーネントを分離する
- Suspense に key をつける
Suspense に key をつけることで、一度 suspend したものとは別のコンポーネントになるため再度 Suspense されます。(ここあたりの認識が曖昧なので間違っていたらコメント下さい🙇) 今回は searchParams
この 2 点の修正を加えることで SearchParams を変更しても Loading を走らせることが出来ます!

注意点として、layout.tsx
workaround でしたが上記の 2 点の修正をすることで SearchParams を変更しても Loading を走らせることが出来ました。 詰まったときは 「なんで URL が変わってるのに Loading されないんだ🤔」と思いましたが、ドキュメントにも書いている通り
An instant loading state is fallback UI that is shown immediately upon navigation.
なのでナビゲーション直後の loading のみを扱うためのようです。(とは言え URL は変更されてるからナビゲーション直後な気もする...) 今回の workaround として変更を入れている部分は Streaming with Suspense で対応する場所なのかもしれません。
CoeFontPublicationAI音声プラットフォーム「CoeFont(コエフォント)」の公式テックブログです。

今日のまとめ
AIデイリーブリーフで今日の重要ニュースをまとめ読み