react-infinite-scrollerで無限ロードを実現する
Next.js
React Infinite Scroll Component
フロントエンド
知識
特筆事項は特にないが、hasMoreのハンドリングをしっかりしていないと、データ取得後にずっとロード中のUIが表示されてしまうため注意
tsx
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>
);
};ts
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;
});
};