원래는 pageNation으로 구현하려 했지만 상품이외의 기능들은 다 pageNation으로 구현이 되어있어서
react-query를 공부할겸 인피니티 스크롤을 사용 해보고 싶어서 적용하기로 결정했다.
상품정보를 나타내는 페이지에서 react-infinite-scroll-component,
React-Query에서 지원하는 useInfiniteQuery를 병합하여 사용해보자
useInfiniteQuery는 파라미터의 값만 변경하여 동일한 useQuery를
반복적으로 호출하기 위한 API이며,useQuery와 같은 형태로 사용됨
const {
fetchNextPage,
fetchPreviousPage,
hasNextPage,
hasPreviousPage,
isFetchingNextPage,
isFetchingPreviousPage,
...result
} = useInfiniteQuery({
queryKey,
queryFn: ({ pageParam = 1 }) => fetchPage(pageParam),
...options,
getNextPageParam: (lastPage, allPages) => lastPage.nextCursor,
getPreviousPageParam: (firstPage, allPages) => firstPage.prevCursor,
})
// 공식문서 예제//
https://tanstack.com/query/v4/docs/react/reference/useInfiniteQuery 공식문서
제공하는 함수들의 자세한 내용과 사용법은 공식문서에 있으니 잘 찾아보자
const [page, setPage] = useState(0); //page 기본값 설정//
const { data, isLoading, fetchNextPage, hasNextPage } = useInfiniteQuery(
["getProducts"],
async ({ pageParam = page }) => {
const response = await api.post("/cal/v1/product", {
filter: [],
page: pageParam,
query: "",
size: 12,
sort: [{ field: "price", option: "desc" }],
});
return response.data.body.product.items;
},
{
refetchOnWindowFocus: false,
getNextPageParam: (lastPage, allPages) => {
if (lastPage.length === 0) {
return false;
}
return lastPage.length;
},
}
);
https://www.npmjs.com/package/react-infinite-scroll-component 공식문서
npm install --save react-infinite-scroll-component //설치
사용할 수 있는 기능이 엄청 많은데 나는 인피니티 스크롤기능만 필요하기 때문에
endMessage 부분 까지만 사용할 것이다.
// return //
<Container sx={{ py: 8 }} maxWidth="xl">
{data ? (
<InfiniteScroll
dataLength={productItems.length} //표시된 데이터 개수//
next={handleLoadMore} //인피니티 스크롤로 값을 더 가져오는 함수//
hasMore={hasMore} //받아올 값이 남았는지 true : false//
loader={<Loading />} //로딩중일 때 로딩상태 표시//
endMessage={ //더이상 가져올 데이터가 없을 때 리턴값//
<>
<p style={{ fontSize: "40px" }}>
<Divider sx={{ marginBottom: "20px" }} />
NO MORE PRODUCTS
</p>
</>
}
>
<Grid
container
spacing={4}
gap={2}
rowGap={4}
sx={{ justifyContent: "center" }}
>
{productItems.map((card) => (
<Grid key={card.id} xs={12} sm={6} md={2.9}>
/* 생략 */
</Container>
원래는 data.pages.map 으로 바로 리턴해주었는데, 이런 오류가 발생한다.. 렌더링이 너무 많이 일어나서 과부하가 걸린거 같아 useEffect를 사용하여 data에 값이 들어올 때 마다 생성한 배열에 값을 담아주고, 값이 담긴 배열을 map으로 리턴해주는 방식으로 변경했더니 해당 오류는 사라졌다.
const [productItems, setProductItems] = useState<ProductType[]>([]);
useEffect(() => {
if (data) {
setProductItems((prevItems) => [...prevItems, ...data.pages.flat()]);
setHasMore(hasNextPage ?? false);
}
}, [data]);
const handleLoadMore = () => {
if (!isLoading && hasMore) {
fetchNextPage().then(({ data }) => {
setPage(Number(data?.pageParams));
setHasMore(hasNextPage ?? false);
});
}
};
endMessage까지 잘 작동한다!
저처럼 useInfinityQuery + 인피니티 스크롤 구현하시는 분들은 react-query의 자동 fetching 기능은 모두 끄고 진행하시길 .. 값이 들어올 때 마다 인피니티 스크롤이 정상 작동하여 값을 가져오는건지 react-query의 내부적인 focus 등의 이벤트로 자동 fetching이 되는건지 구분이 잘 안갑니다 ㅜ
{
refetchOnWindowFocus: false,
}
저는 해당 옵션만 추가하여 진행했습니다.