react-infinite-scrollerで無限ロードを実現する
Next.js
react-infinite-scroller
特筆事項は特にないが、hasMoreのハンドリングをしっかりしていないと、データ取得後にずっとロード中のUIが表示されてしまうため注意
export const MemoContainer = (): JSX.Element => { const [memos, setMemos] = useState<Memo[]>([]); const [memoPage, setMemoPage] = useState(1); const [memoHasMore, setMemoHasMore] = useState(true); const { data: memosData, isLoading, isError, } = useGetMeMemoLikes({ page: memoPage }); useEffect(() => { if (memosData && memosData.memos.length === 0) { setMemoHasMore(false); } }, [memosData]); if (isError) { errorToast('メモの取得に失敗しました。'); } if (isLoading) { return <LinearLoading />; } return ( <InfiniteScroll pageStart={0} loadMore={() => { refetchData<Memo>( memosData?.memos, memosData?.total, memoPage, setMemos, setMemoPage, setMemoHasMore ); }} initialLoad={true} hasMore={memoHasMore} loader={<SimpleSpinner className='mt-8' key='memo-loader' />} > <MemoList memos={memos} /> </InfiniteScroll> ); };
export const refetchData = <T>(
newData: T[] | undefined,
total: number | undefined,
page: number,
setData: React.Dispatch<React.SetStateAction<T[]>>,
setPage: React.Dispatch<React.SetStateAction<number>>,
setHasMore: React.Dispatch<React.SetStateAction<boolean>>
): void => {
if (!page || !newData || !total) return;
if (newData.length === 0) {
setHasMore(false);
return;
}
setData((prevData) => {
// 重複データを除外
const filteredNewData = newData.filter(
(newItem) => !prevData.some((prevItem) => equals(prevItem, newItem))
);
const mergedData = [...prevData, ...filteredNewData];
if (mergedData.length >= total) {
setHasMore(false);
} else {
setPage(page + 1);
}
return mergedData;
});
};