🐡

ページ全体の再レンダリングなしでクエリパラメータを変更したい(AppRouter)

2024/08/02に公開

経緯

外部にも状態を保持したまま URL を共有できることが要件としてあるプロジェクトで、その要件を内部の state の一部を URL に反映したり、該当の URL にアクセスされた場合はクエリパラメータから状態を復元して表示して実現していました。

ただし、過去に React 17 + react-router で作ったプロジェクトだったので、今回 Nextjs(App Router)に載せ替えたところ、クエリパラメータを変更するとページ全体に再レンダリングが発生してしまう問題に遭遇したので、ページ全体を再レンダリングせずに状態を更新する方法を模索しました。

どこから探そう?

まずはnextjs app router change without renderingとググってみました。

https://stackoverflow.com/questions/64548544/how-to-change-route-without-re-rendering-in-next-js

なるほど。shallowオプションを使えるとのこと。

shallow routing(浅いルーティング)とは

データフェッチメソッドを再度実行せずに URL を変更できる仕組みとのこと。

https://nextjs-ja-translation-docs.vercel.app/docs/routing/shallow-routing

Pages Router では shallow オプションが使える

Nextjs(Pages Router)を使用している場合は router.push の第三引数に shallowオプションがあります。

router.push("/?counter=10", undefined, { shallow: true });

https://nextjs.org/docs/pages/building-your-application/routing/linking-and-navigating#shallow-routing

App Router ではどうしたらいい?

next app router shallow routingでググってみると先人が智慧を書き残しておいてくださっていました。

https://zenn.dev/kimizuy/articles/approuter-shallow-routing

実践

// 絶対パスを取り出す
const baseUrl = new URL(window.location.origin);
const url = new URL(`/`, baseUrl);
const query = { id: 1, param:1 }
Object.keys(query).forEach((key) => {
  // 今回は複数のクエリを指定する必要があったのでループさせてsearchParamsにセット
  url.searchParams.set(key, query[key]);
});
// history.pushState()を使ってクエリパラメータを更新
window.history.pushState({}, "", url);

確かにApp Routerでも全体が再レンダリングされずにクエリパラメータを変更できました。

参考

https://developer.mozilla.org/en-US/docs/Web/API/History/pushState
https://github.com/vercel/next.js/discussions/48110

Discussion