React-Query Infinite Scroll 사용하기

김태영·2023년 6월 26일
0

원래는 pageNation으로 구현하려 했지만 상품이외의 기능들은 다 pageNation으로 구현이 되어있어서
react-query를 공부할겸 인피니티 스크롤을 사용 해보고 싶어서 적용하기로 결정했다.

상품정보를 나타내는 페이지에서 react-infinite-scroll-component,
React-Query에서 지원하는 useInfiniteQuery를 병합하여 사용해보자

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;
      },
    }
  );

React-infinite-scroll-component

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,
}

저는 해당 옵션만 추가하여 진행했습니다.

profile
구렁이

0개의 댓글