[NextJS]ISRのfallbackの挙動が難しかった
概要
ISRを使用した際のgetStaticPaths
のfallback
の各モードについて、挙動が難しかったのでまとめることにします。
理解に苦しんだので認識が違っていましたら、教えていただけると幸いです。
おそらく初めてISRの実装をする方はrevalidate
やpath
に比べてfallback
の挙動を理解するのに苦労すると思います。
getStaticPathsのfallbackについて
getStaticPathsのfallbackのモードにはfalse
、true
、blocking
の3種類あり、false
は理解できるがtrue
とblocking
の挙動は若干難しいです。
・サンプルコード
import { NextPage, GetStaticPaths, GetStaticProps } from 'next';
interface Props {
post: Post
}
const Page: NextPage<Props> = ({post}) => {
return (
<p>{post.title}</p>
)
}
export const getStaticProps: GetStaticProps<Props> = async () => {
const {post} = await fetchPost();
return {
props: {
post
},
revalidate: 60,
}
}
export const getStaticPaths: GetStaticPaths = async () => {
return {
paths: [
{ params: { id: '1' } },
],
fallback: 'false',
}
}
export default Page;
fetchPostの引数なしでpostを一件取得するというツッコミどころはありますが、そちらは一旦置いといてもらえると助かります。
false
の挙動
この例ですと
paths: [
{ params: { id: '1' } },
],
fallback: 'false',
でビルド時にpost/1のHTMLを生成しています。(revalidateは1分)
するとたとえばpost/2などビルド時にHTMLを生成していないページを指定(リクエスト)した際は404エラー(page)となります。
なのでダイナミックルートのページなどにpathを指定してfallbackをfalseに指定することは少ないと思います。
true
の挙動
ダイナミックルートなどのページではpathを指定せずtrueかblockingを指定します。
paths: [],
fallback: 'true'//or blocking,
この場合ビルド時にはどのpathのHTMLも生成せず、遷移時には外部データが取得されていない状態でHTMLをクライアントに返し、その後サーバで外部データを取得してHTMLを生成しレンダリングされたのち生成したHTMLをキャッシュします(1分間)。
流れを整理すると
①外部データがない状態のHTMLを表示
②サーバーサイドで外部データを取得しクライアントをレンダリングする
③②で生成したHTMLをキャッシュし次回以降はそのHTMLを返す
となります。
となるとPropsのpostは最初は空のオブジェクト({})になるのでこのままですと以下の箇所でtypeエラーとなります。
<p>{post.title}</p>
fallback
がtrue
の際の対処法
まず一つがpostが空の場合の分岐をクライアントで作ることが考えられます。
<p>{post ? post.title : '...fallback中'}</p>
またこれと似てはいますが、router.isFallback
の判定を入れる方法も考えられます。
import { useRouter } from "next/router"
interface Props {
post: Post
}
const Page: NextPage<Props> = ({post}) => {
const router = useRouter()
return (
{router.isFallback ? (
<p>...fallback中</p>
) : (
<p>{post.title}</p>
)}
)
}
そしてもう一つの対処法がblocking
になります。
blocking
の挙動
blocking
はその名の通りfallback
をブロック(fallbackを行わない)します。
どういう挙動かというと、
paths: [],
fallback: 'blocking',
とした場合、ビルド時にどのpathのHTMLも生成されていないのはtrueと同じです。
ですが初回の遷移の挙動が異なります。
blocking
の場合、初回遷移時にサーバー側で完全なHTMLが生成されるまでHTMLを返しません。
つまり、初回はSSRと同じ挙動になり、次回以降はキャッシュされたHTMLを表示します。
流れは以下のようになります
①遷移時(初回)にSSRで完全なHTMLを生成しクライアントに返却(キャッシュもする)
②次回以降キャッシュを表示
となります。
getStaticPropsで外部データの取得がエラーとなった際
これは僕がつまったところでもあるが、もしgetStaticPropsの中での外部データの取得が失敗した場合、 どういった挙動になるのかである。
公式ドキュメントに記載がありました。
If there is an error inside getStaticProps when handling background regeneration, or you manually throw an error, the last successfully generated page will continue to show. On the next subsequent request, Next.js will retry calling getStaticProps.
getStaticProps内でエラーが発生した場合、または手動でエラーをthrowした場合、最後に正常に生成されたページが引き続き表示されます。そして後続のリクエストでgetStaticPropsの呼び出しを再試行します
つまり外部データの取得に失敗した際は、エラーをthrowすれば前回までの正常に生成されたHTMLを表示してくれるらしい...
知らなかった...
ぜひうちではgetStaticPropsでのエラーハンドリングはこうしてるよ!などがありましたら教えてください!
参考
Next.js 10 リリースノート全訳! 画像の自動最適化、i18n対応、アナリティクス、Eコマースほか
nextjsのISRを使うときのfallback指定について理解するまでの話
NextJS公式
Discussion