Open2
swrを利用した双方向ページネーション
swr には useSWRInfinite
という hook があり、これを用いたページネーションが実装できる。
ただ useSWRInfinite
は単方向のページネーションをサポートしているが、双方向のページネーションはサポートしていない。
ちなみに react-query では双方向のページネーションをサポートしている。
双方向のページネーションが必要な要件がある場合は react-query を採用すれば基本的には良いかなと思う。
ただ swr でなんとか双方向ページネーションに対応できないかなと試行錯誤していて、それっぽいものが出来た。
useSWRSubscription
と eventmit
を利用し、初回のデータ取得は subscribe
関数内でそのまま行い、前後ページのデータ取得は eventmit
によるトリガーを起点に fetch している。
import { eventmit } from "eventmit";
import { useCallback, useMemo } from "react";
import useSWRSubscription from "swr/subscription";
import { MessagesResponse } from "../../features/message/type";
const endpoint = "/api/messages";
export const useMessages = () => {
const nextEvent = useMemo(() => eventmit<{ cursor: string }>(), []);
const prevEvent = useMemo(() => eventmit<{ cursor: string }>(), []);
const result = useSWRSubscription({ endpoint }, (args, { next }) => {
fetch(args.endpoint)
.then((res) => res.json())
.then((d: MessagesResponse) => {
next(null, d);
});
nextEvent.on((value) => {
fetch(`${args.endpoint}?next=${value.cursor}`)
.then((res) => res.json())
.then((d: MessagesResponse) => {
next(null, (prev: MessagesResponse) => {
return {
...prev,
data: [...prev.data, ...d.data],
nextCursor: d.nextCursor,
};
});
});
});
prevEvent.on((value) => {
fetch(`${args.endpoint}?prev=${value.cursor}`)
.then((res) => res.json())
.then((d: MessagesResponse) => {
next(null, (prev: MessagesResponse) => {
return {
...prev,
data: [...d.data, ...prev.data],
prevCursor: d.prevCursor,
};
});
});
});
return () => {
nextEvent.offAll();
};
});
const fetchNextPage = useCallback(
(cursor: string) => {
nextEvent.emit({ cursor });
},
[nextEvent],
);
const fetchPrevPage = useCallback(
(cursor: string) => {
prevEvent.emit({ cursor });
},
[prevEvent],
);
return { ...result, fetchNextPage, fetchPrevPage };
};
ただし useSWRSubscription
の本来の用途からは少し外れている気はする。