lottie
Seungjun's blog
blog
react에서 react-query를 이용한 무한 스크롤을 구현해보자

주의 이 글은 기본적인 react-query 사용법을 알고 보시는것을 추천드립니다.

react에서 react-query를 이용한 무한 스크롤을 구현해보자

일반적으로 react-query를 이용해 서버 상태를 관리할때 useQuery를 사용한다. 내가 작성한 뉴욕 타임즈 api를 활용한 부분에서 예를 들어보겠다. (Github : https://github.com/seungjun0423/times-scrap)

import { useInfiniteQuery } from "@tanstack/react-query";

const {
    data
  } = useInfiniteQuery({
        queryKey:['todayHeadline'], 
        queryFn: ({pageParam})=>fetchFn({pageParam}), 
    });

위 코드를 컴포넌트 내에서 사용해주면 간단히 데이터를 가져올 수 있다. react-query의 여러 특성을 설명하면 글이 길어지니 내가 공부하며 느꼈던 특이한점 한가지만 적자면 여기서 실질적으로 데이터를 호출하는 함수인 fetchFn (axios 또는 fetch등을 이용해 직접 서버와 통신하는 함수)은 react-query에서 비동기 관련 작업들을 해주기 때문에 async,await 등 비동기적으로 작성해줄 필요가 없다. 그냥 Promise를 반환해주면 data로 선언한 부분을 이용해 response를 받아올 수 있다.


어쩄든 기본 방식이 위의 useQuery이고 이와 함께 useEffect를 사용해 무한 스크롤을 구현할 수도 있다. 하지만, react-query에서는 무한스크롤을 간편하게 구현할 수 있도록 useInfiniteQuery라는 기능을 제공한다.

위 코드를 useInfiniteQuery로 바꿔보면 아래와 같다.

import { useInfiniteQuery } from "@tanstack/react-query";

const {
    data,
    isFetchingNextPage,
    fetchNextPage,
    hasNextPage,
  } = useInfiniteQuery({
        queryKey:['todayHeadline'], 
        queryFn: ({pageParam})=>fetchFn({pageParam}),
        getNextPageParam: ( lastPage, allPages ) => Number(allPages.length)+1,
    });

여기서 중요한 점은 getNextPageParam: (lastPage, allPages) => unknown | undefined 이 부분이다. 이 부분을 중점적으로 코드를 살펴보자

  1. getNextPageParam(lastPage, allPages) :

    lastPage : 지금껏 요청한 모든 데이터중 가장 마지막에 받아온 데이터

    allPages : 모든 데이터 요청의 parameter 배열

  2. isFetchingNextPage : boolean 값을 리턴하며 이름 그대로 다음 데이터 요청이 진행중인지 알려준다

  3. fetchNextPage : getNextPageParam에서 리턴해주는 다음 요청을 위한 parameter를 받고 이를 fetchFn의 인자로 넣어서 실행시켜준다.

  4. hasNextPage : boolean 값을 리턴하며 요청할 다음 데이터가 있는지 판단한다.

여기 까지 작성하면 react-query에서 필요한 부분은 모두 작성되었다. 이후 IntersectionObserver API를 이용해 아래의 코드를 작성하고 Ref dom을 붙이면 끝! 이부분도 하고 싶은 말이 많지만 우선 react-query 글이기에 이정도만 이야기 하겠다.

useEffect(() => {
    if (loadingRef.current && hasNextPage) {
    const observer = new               IntersectionObserver( 
        entries => 
        entries[0].isIntersecting &&                fetchNextPage(),
        { threshold: 1 }
    );
    observer.observe(loadingRef.current);
    return () => observer.disconnect();
    }
}, [hasNextPage]);