マウント時のSWRキャッシュ更新処理の制御
SWRを使用している際に、オプションの設定誤りでマウント時のキャッシュ更新処理が意図しない動きをすることがありました。キャッシュデータの有無によって意図しないリクエストが飛んでしまうため、オプションを適切に設定しておきたいところです。公式ドキュメントはこの辺が少し分かりづらいため共有したいと思います。
マウント時の更新処理に関わるオプション
マウント時のキャッシュ更新処理を制御するオプションは以下の2つです。
revalidateOnMountrevalidateIfStale
これらの設定値の組み合わせで動作が変わるので詳しく見ていきます。
revalidateOnMount
revalidateOnMountオプションは、マウント時に再検証を行うかどうかを設定します。
-
true: 再検証を常に行う -
false: 再検証を常に行わない
このオプションだけでマウント時の制御が決まりそうですが、上記オプションはデフォルトでは未設定となっています。その場合は次のrevalidateIfStaleによって制御されます。
revalidateIfStale
revalidateIfStaleオプションは、既にキャッシュデータが存在している場合に再検証するか否かを決定します。
-
true(デフォルト): キャッシュデータがあっても更新を行う -
false: キャッシュデータがあれば更新を行わない
つまり、これらのデフォルト状態のオプションは場合、revalidateOnMount:未設定, revalidateIfStale:trueなので、マウント時には必ず再検証が行われます。
制御ケース
以上を踏まえてそれぞれの制御ケースをこれらの組み合わせで考えたいと思います。
どんなときもマウント時は再検証しない
useSWR('/api/data', fetcher, { revalidateOnMount: false })
revalidateOnMountをfalseとしているので、revalidateIfStaleの設定に関わらずマウント時の再検証は発生しなくなります。
上記は、mutateAPIなどで再検証タイミングを制御したい場合に利用できます。
マウント時は必ず再検証する
useSWR('/api/data', fetcher, { revalidateOnMount: true })
// or
useSWR('/api/data', fetcher, { revalidateOnMount: undefined, revalidateIfStale: true })
// or
useSWR('/api/data', fetcher, { revalidateIfStale: true }) // デフォルト
上記はどれもマウント時は常に再検証される設定になります。
ただし、{ revalidateIfStale: true }の場合revalidateOnMountが未設定のため、他のSWR設定やグローバル設定によって上書きされる可能性があることに注意が必要です。
function MyComponent() {
const { data, error } = useSWR('/api/data', fetcher, { revalidateIfStale: true })
// ...
}
function App() {
return (
<SWRConfig
value={{
revalidateOnMount: false, // グローバル設定でマウント時の再検証を無効にする
}}
>
<MyComponent />
// ...
</SWRConfig>
)
}
キャッシュデータが存在していない場合だけ再検証する
useSWR('/api/data', fetcher, { revalidateIfStale: false })
revalidateOnMountは設定せず、revalidateIfStaleをfalseとしているので、revalidateIfStaleの設定に関わらずマウント時の再検証は発生しなくなります。
ただし、こちらもrevalidateOnMountを明示的に設定していないのでグローバル設定によって上書きされる可能性があります。上書きを防止したい場合は上記と同様に明示的にrevalidateOnMount: undefinedとする必要があることに注意してください。
まとめ
SWRはキャッシュ制御を便利してくれますが、オプションが豊富で再検証を自動で行うなど意図せず検証処理が走ることがあります。効率的なデータ取得とパフォーマンスの最適化を実現するために適切にSWRの設定を行うとよいかと思います。
Discussion