SSRの初回アクセス以降のnext/linkのリンクやnext/routerなどによるページ遷移時に、getServerSidePropsの計算などの待機時間が生じます。
今回はそのようなページ遷移待機中を検知してローディングを表示する実装方法についてまとめていきます。
SSRでの初回アクセス・2度目以降のアクセス時の動作の差分・仕組みはこちらのページで説明しているのでよかったら見てみてください!
目次
ページ遷移待機中とはどのタイミングか?
ページ遷移待機のタイミングはどこか?
リンクなどで遷移をしようとした時から、サーバーサイド側でgetServerSidePropsや差分jsなどを用意して返却するまでの間です。
Nuxt.jsだとこのローディングはFW側でローディングコンポーネントが表示されるっぽいです。
しかし、Next.jsにはそのような機能がないため、待機時間が長かったり待機中にUIに動きがないことが気になる場合は自前でローディングコンポーネントなどを表示する必要があります。

ページ遷移待機中のタイミングを検知してローディング表示
以下のコードをpages/_app.tsxに配置することでローディング中かどうかを検知してローディングコンポーネントを表示できます。
_app.tsxは各ページのルートコンポーネントです。<Component {…pageProps} />が各ページの内容になります。
ここでルーティングの変更を検知してStateを変更することでページ遷移待機中かどうか判定ができます。
import { useRouter } from "next/router"
import { useEffect, useState } from "react"
export default function MyApp({Component, pageProps}) {
const router = useRouter()
const [pageLoading, setPageLoading] = useState(false)
useEffect(() => {
const handleStart = (url) => url !== router.asPath && setPageLoading(true)
const handleComplete = () => setPageLoading(false)
router.events.on('routeChangeStart', handleStart)
router.events.on('routeChangeComplete', handleComplete)
router.events.on('routeChangeError', handleComplete)
return () => {
router.events.off('routeChangeStart', handleStart)
router.events.off('routeChangeComplete', handleComplete)
router.events.off('routeChangeError', handleComplete)
}
})
// TODO 正式なローディングコンポーネントにする
const loadingComponent = (<h2>Loading...</h2>)
return (<div>
{pageLoading && loadingComponent}
<Component {...pageProps} />
</div>)
}
next/routerのrouter.events.on()でルーティング変更・完了・エラーのイベントを拾うことができます。
ルーティング開始はrouteChangeStart、完了はrouteChangeComplete、エラー時はrouteChangeErrorです。
routeChangeStartでローディングを示すstateをtrueにして、routeChangeComplete/Errorでローディングのstateをfalseにします。
ListenerはuseEffectのcleanup関数で解除しておくようにしましょう。
ローディングのstateを各ページコンポーネントなどで見れるようにしたい場合はReact.Contextが必要になります。
Next.jsでReact.Contextを使う方法については以下の記事にまとめているのでよかったら見てみてください!