TanStack Query v5で廃止された refetchPage の代替実装
こんにちは、koyablueです。
最近TanStack Queryのv4 → v5へのアップデート作業を行いました。その際、useInfiniteQueryのrefetchPageというオプションが廃止されており、v4までの「ページを選別してリフェッチする」という挙動をどう置き換えるべきか、いろいろと調査が必要になりました。
この記事ではその調査内容と代替実装をまとめます。
useInfiniteQueryとは
TanStack Queryが提供するフックで、無限スクロールや「もっと読み込む」など、ページを追加取得して積み上げていくタイプのUIを実装したい場合に使用されます。
refetchPage
useInfiniteQueryが返すrefetchに渡せるオプションです。
どのページを再取得するかを (page, index) => booleanで指定でき、例えばindex === 0とすれば 先頭ページだけを再取得できます。
v4のドキュメントにも先頭ページだけを再取得するサンプルコードが載っており、実運用でも使われがちなパターンだと思います。
私たちの実装でも、同じように先頭ページだけ再取得するというユースケースで利用していました。
v5で廃止
このオプションはv5で廃止されており、公式のマイグレーションガイドにはrefetchPageを廃止してmaxPagesを導入したという記述があります。
ただこのmaxPagesはrefetchPageをそのまま代替するものではないようで、ドキュメントを見ると
The maximum number of pages to store in the infinite query data.
When the maximum number of pages is reached, fetching a new page will result in the removal of either the first or last page from the pages array, depending on the specified direction.
と説明されています。つまり、maxPagesはinfinite queryのdataが保持するページ数の上限を設定するためのオプションです。refetchPageの単純な代替というわけではありません。
代替実装
ではv4のようなページ選別リフェッチはv5で不可能になったのかというと、そういうわけではないようです。直接の代替APIはありませんが、workaroundが紹介されているdiscussionを発見しました。
const resetInfiniteQueryPagination = (): void => { queryClient.setQueryData(queryKey, (oldData) => { if (!oldData) return undefined; return { ...oldData, pages: oldData.pages.slice(0, 1), pageParams: oldData.pageParams.slice(0, 1), }; }); };
const onRefresh = () => { resetInfiniteQueryPagination(); refetch(); }
このworkaroundは何をしているのか
キャッシュを先頭ページだけに切り詰めています。
pageとpageParamはuseInfiniteQueryが返すdataの型です
interface InfiniteData<TData, TPageParam = unknown> {
pages: Array<TData>;
pageParams: Array<TPageParam>;
}
pagesで各ページのデータの配列を持ち、pageParamsにページパラメータの配列を持ちます。
このpagesとpageParamsの配列に対して.slice(0, 1)することで先頭だけ残し、queryClient.setQueryDataを使ってキャッシュを更新します。
例えば、3ページ分読み込んでいた場合、
pages: [page1, page2, page3], pageParams: [1, 2, 3]
↓
pages: [page1], pageParams: [1]
のようになるイメージです。
その後、キャッシュに先頭ページだけ残った状態でrefetch()を行うことで、先頭ページだけ再取得するという挙動を実現しています。
実際に使う時はカスタムフックにまとめるとよさそうです。
// 例)
const { postInfiniteQuery, refetchOnlyFirstPage } = usePostsInfiniteQuery();
なぜこのworkaroundが適切と言えるのか
メンテナーが以下のようにコメントしているので。

yes, this is the best way to handle the case.
おわりに
v5.0.0がリリースされたのが2023年なのでかなり今更感がありますが...今後v5にアップデートする方の一助になれば幸いです🙏
Discussion