#はじめに
React QueryのuseInfiniteQuery()
で無限スクロールを実装したので記事にまとめました。
「React Queryって何?」という方は、よろしければ前回の記事もご覧ください。
#useInfiniteQueryとは
useInfiniteQuery()
はデータの一部で返される次のクエリを追跡します。
useQuery()
とは返すdata
の中身が異なり、pages
とpageParams
の2つのプロパティを含んだオブジェクトとなります。
pages
はクエリの結果を配列で返し、pageParams
は実行されたクエリのキーを配列で返します。
上がpages
、下がpageParams
の結果の例となります。
また、useInfiniteQuery()
の記述例は以下のようになります。
const {
data,
fetchNextPage,
hasNextPage,
isLoading,
isFetching,
isError,
error,
} = useInfiniteQuery(
'sw-people',
({ pageParam = initialUrl }) => fetchUrl(pageParam),
{
getNextPageParam: (lastPage) => lastPage.next || undefined,
}
);
pageParam
には初期値としてdefaultUrl
が設定されていますが、optionであるgetNextPageParam
によって更新されます。
以上の例では、最新ページのデータlastPage
から次のpageParam
をlastPage.next || undefined
として設定していますが、allPages
というパラメータも利用することができます。
このように、pageParam
の現在値はReact Queryによって管理されます。
また、useInfiniteQuery()
が返すオブジェクトのプロパティには、useQuery()
にはなかった以下のようなものが存在します。
-
fetchNextPage
: 次ページのデータのフェッチを許可する関数 -
hasNextPage
:getNextPageParam
によってpageParam
が更新されたらtrue
を返す(undefinedのときfalse
) -
isFetchingNextPage
:fetchNextPage
で次ページのデータをフェッチしている間true
を返す(loadingのフラグに使用)
これまでに登場したワードを実際のデータ取得フローあてはめると以下のようになります。
- コンポーネントがマウントされて最初のページをフェッチする
-
getNextPageParam
がpageParam
を更新する -
hasNextPage
がtrue
であれば、イベント(スクロールやボタンクリック)によってfetchNextPage
を実行する -
hasNextPage
がundefined
になるまで2→3を繰り返す
#実装例
useInfiniteQuery()
とReact Infinite Scrollerを使って、無限スクロールを実装します。
import InfiniteScroll from 'react-infinite-scroller';
import { useInfiniteQuery } from 'react-query';
import { Person } from './Person';
const initialUrl = 'https://swapi.dev/api/people/';
const fetchUrl = async (url) => {
const response = await fetch(url);
return response.json();
};
export function InfinitePeople() {
const {
data,
fetchNextPage,
hasNextPage,
isLoading,
isFetching,
isError,
error,
} = useInfiniteQuery(
'sw-people',
({ pageParam = initialUrl }) => fetchUrl(pageParam),
{
getNextPageParam: (lastPage) => lastPage.next || undefined,
}
);
if (isLoading) return <div className="loading">Loading...</div>;
if (isError) return <div>Error! {error.toString()}</div>;
return (
<>
{isFetching && <div className="loading">Loading...</div>}
<InfiniteScroll loadMore={fetchNextPage} hasMore={hasNextPage}>
{data.pages.map((pageData) => {
return pageData.results.map((person) => {
return (
<Person
key={person.name}
name={person.name}
hairColor={person.hair_color}
eyeColor={person.eye_color}
/>
);
});
})}
</InfiniteScroll>
</>
);
}
react-infinite-scroll
からInfiniteScroll
をインポートして、Person
コンポーネントをラップします。
loadMore
プロパティにfetchNextPage
を、hasMore
プロパティにhasNextPage
を設定することで次ページのデータのフェッチを制御することができるようになります。
#参考資料