社内のあるプロダクトで Next.js を v13 系から v15 系にアップデートしたところ、トップページにブラウザバックで戻ると、エラーが発生するようになってしまった。
エラーの原因を調べてみると、何故かトップページにブラウザバックで戻った時、pageProps
が空オブジェクトになっているようだった。
実はこれは Next.js の不具合で、以下に issue がある (まあ僕が報告したのだけど...)。
Next.js 側で修正されるのを待ってたのだけど、不具合の報告から1年以上経っても何の音沙汰もない。困り果てたので、なんとか不具合を回避する方法がないか、探すことにした。
不具合の発生条件
この不具合はいつでも再現する訳ではなくて、以下の条件をすべて満たす時発生するようになってる。
- Next.js 14.0.0+ を使っている
basePath
オプションを使っている- Middleware を使っている
- トップページを Pages Router でレンダーしている
- トップページへ soft navigation で遷移する
すべて満たす時発生するので、どれかが欠けると発生しない。つまり発生要件のどれかを満たさないようにすれば、問題を回避できるはず。そう考えて、どれかを満たさないようにする方法がないか模索することにした。
回避策の検討
基本的には、以下のどれかが回避策として利用できるはず。
- 方法1:
basePath
オプションの使用をやめる - 方法2: Middleware の使用をやめる
- 方法3: Pages Router の代わりに App Router でトップページをレンダーする
- 方法4:トップページへ hard navigation で遷移する
ただ方法1については、我々のプロダクトではbasePath
オプションがどうしても必要なプロダクトだったため、採用できなかった。方法2も、プロダクトの重要なところで Middleware を使っていて他の技術への置き換えが難しく、採用できなかった。
方法3は将来性があって優れてるけど、Pages Router から App Router に移行するのが大変なのが気がかりだった。我々のプロダクトでは getServerSideProps
に複雑なロジックが書かれているのだけど、getServerSideProps
は App Router にはない。App Router では getServerSideProps
に書いていたロジックを Server Component に書くことになる。だいぶ書き方が違うため、どうにも移行が難しかった。他に良い方法がないなら選択しても良いけど、気軽に選びにくい方法だった。
他に何か良い方法がないか...と悩みに悩んで思いついたのが方法4だった。トップページへ soft navigation で遷移すると不具合が発生するのだから、hard navigation で遷移したら不具合は発生しないはず。これならアプリケーションの機能を殆ど損なうこともなく、大幅なコードの書き換えもなく導入できるはず。そう考えて、方法4を採用することにした。
回避策の実装
以下に全てが書いてあるので、それを見て欲しい!
重要なポイントは以下。
<Link>
component をラップし、トップページの遷移を hard navigation に fallback させるimport Link from 'next/link'
をimport {Link} from '@/components/Link'
に置き換える
useRouter()
をラップし、トップページの遷移を hard navigation に fallback させるimport {useRouter} from 'next/link'
をimport {useRouter} from '@/lib/useRouter'
に置き換える
Router.beforePopState
を使用し、トップページへのブラウザバック/フォワードをキャンセルし、ハードナビゲーションに fallback させる
とにかく soft navigation でトップページに遷移してしまう経路を握りつぶしたら良い。import 文の書き換えがちょっと面倒ではあるけど、 ts-fix で足りない import を自動で追加してみる のような手法で機械的に書き換えたら良いはず。
一応手元で確認した限りは、これで問題なく不具合を回避できた。
おわりに
この記事で紹介した回避策は、僕の社内のプロダクトで必要に迫られて導入したものだったけど、多分世の中にも必要なプロダクトがたくさんあると思う。つい最近もかなり古い Next.js バージョンを使ってる、basePath
を利用するプロダクトを見かけた。真相はわからないけど、僕と同じような不具合にハマってバージョン上げられてないのかなと想像していた。
この記事がそういう人のためになればと思う。
おまけ
basePath オプションは他にも奇妙な不具合がある。Pages Router / basePath
/ rewrites
/ Middleware 併用時に発生する以下の不具合とか。何故かある長さより小さいパスのページにするとエラーになる。5文字のパスのページには遷移できるけど、4文字のパスのページには遷移できない、という不思議すぎる挙動をする...
basePath
が悪いのか、Pages Router を使っているのが悪いのか分からないけど、とにかくそれ関連の組み合わせがめちゃめちゃバグっている印象がある。Pages Router がもうアクティブに開発されないことを考慮すると、今後も新しい Next.js で basePath
/ Pages Router 併用時に発生する新たなバグが混入して、それが修正されず放置される可能性も高いだろう。
将来を見据えるなら、今回の記事で紹介したような一時しのぎ的な解決策ではなく、App Router への移行をすべきだと思う。あと basePath 使わずに済むならそれに越したことはない。まあそれが難しいから皆困ってるんだろうけど...