useSWRInfinite
無限スクロールとかをシンプルなAPIで作れる
code:ts
import useSWRInfinite from 'swr/infinite';
params
getKey(index, prevData)
ページごとのkeyを返す関数
fetch処理の前に呼び出される
引数のindexは、読み込むページのindex
引数のprevDataは、前回fetchしてきたデータ
例えばここにhasMoreなどが含まれていれば終了判定として使える
これの返り値がfetcherの引数になる
例えば、return [key, hoge]とすると、fetcher(key, hoge) => {..}として受け取れる
nullを返した場合は、requestが送られない
終了判定として使える
fetcher()
getKey()返り値が、引数として渡される
options
initialSize = 1: number of pages should be loaded initially
revalidateAll
defaultはfalseなんやねmrsekut.icon
always try to revalidate all pages
revalidateFirstPage = true:
always try to revalidate the first page
persistSize = false:
don't reset the page size to 1 (or initialSize if set) when the first page's key changes
Return Values
data
fetchしてきた結果の配列
ページごとの配列になっている
flat()して使うことが多いと思うmrsekut.icon
error: same as useSWR's error
isValidating: same as useSWR's isValidating
mutate: same as useSWR's bound mutate function but manipulates the data array
size
何ページ分のfetchをしたか
ページのindexと言ったほうが明確じゃないか?mrsekut.icon
初期値が1で、読み込むごとに1増えていく
fetchしてきたコンテンツの量の話ではないmrsekut.icon
setSize()
引数のindexのページを読み込む
setSize(size => size + 1)として、次のページを読み込む
こう使うことが多いと思うmrsekut.icon
例
code:ts
const useItems = (userId: UserId) => {
const dispnumber = 30;
// ②この引数に対応する
const fetcher = async (key: string, pageno: number) => {
return await getItems({...}); // :: Item[]
};
const getKey = (pageno: number, previousData: Item[]) => {
if (previousData != null && previousData.length < dispnumber) {
setHasMore(false); // こうすると1回分遅れちゃうんだよな..
return null;
}
return [ /users/${userId}/items?pageno=${pageno}, pageno ] as const; // ①この返り値が、
};
// dataの型はItem[][]
const { data, error, setSize } = useSWRInfinite(getKey, fetcher);
const loadMore = useCallback(() => {
setSize(size => size + 1);
return {
items: data != null ? data.flat() : [], // flatしてItem[]にする
error,
loadMore,
hasMore
};
};
終了判定をどうするか
案1
上のコード例のように、getKeyの中でprevDataを見て、setStateする
しかしこれをやると、最後まで読み込んだ後にも「続きを読み込む」が表示されてしまう
この状態でこれをクリックしたら、何も読み込まれず、このボタンが消える
つまり、1回分遅れる
案2
useEffect内でsetStateする
参考