tanstack-query로 무한스크롤 구현하기 (useInfiniteQuery | nextPageToken | typescript)

이보경·2023년 11월 29일
post-thumbnail

tanstack-query 공식 문서 - useInfiniteQuery
codesandbox에서 useInfinityQuery 기본 코드 연습하기

useInfiniteQuery 기본 코드 살펴보기

주목할 변수는 fetchNextPage, pageParam, getNextPageParam 이다.

const {
  fetchNextPage, // 다음 페이지를 불러오는 함수
  fetchPreviousPage, // 이전 페이지를 불러오는 함수
  hasNextPage, // 다음 페이지가 있는지 여부를 확인하는 함수
  hasPreviousPage, // 이전 페이지가 있는지 여부를 확인하는 함수
  isFetchingNextPage, // 다음 페이지를 불러오는 중인지 여부를 확인하는 변수
  isFetchingPreviousPage, // 이전 페이지를 불러오는 중인지 여부를 확인하는 변수
  ...result // 쿼리 결과를 담고 있는 객체, 기타 옵션들과 함께 사용됨
} = useInfiniteQuery({
  queryKey, // 쿼리를 식별하는 키
  queryFn: ({ pageParam }) => fetchPage(pageParam), //  페이지 파라미터를 받아 해당 페이지를 가져오는 함수
  initialPageParam: "", // v5에서 변경된 초기값 설정
  ...options,
  getNextPageParam: (lastPage, allPages) => lastPage.nextCursor, // 마지막 페이지와 모든 페이지를 받아 다음 페이지의 파라미터를 반환하는 함수
  getPreviousPageParam: (firstPage, allPages) => firstPage.prevCursor, // 첫 페이지와 모든 페이지를 받아 이전 페이지의 파라미터를 반환하는 함수
})

적용하기

우선 lodash의 debounce를 이용해 useHandleScroll을 훅으로 만들었다.

// useHandleScroll.tsx
type fetchNextPageType = (
  options?: FetchNextPageOptions | undefined
) => Promise<InfiniteQueryObserverResult<InfiniteData<any, unknown>, Error>>;

const useHandleScroll = (fetchNextPage: fetchNextPageType) => {
  useEffect(() => {
    window.addEventListener("scroll", scrollFn);
    return () => {
      window.addEventListener("scroll", scrollFn);
    };
  }, []);

  const scrollFn = _.debounce(() => {
    if (
      window.innerHeight + document.documentElement.scrollTop >=
      document.documentElement.offsetHeight - 10
    ) {
      fetchNextPage();
    }
  }, 200);

  return;
};

export default useHandleScroll;

그리고 무한 스크롤을 적용할 컴포넌트에서 다음과 같이 사용한다.

// MainPage.tsx
  const {
    status,
    data: videoList, // data 변수명을 videoList로 변경
    error,
    fetchNextPage,
  } = useInfiniteQuery({
    queryKey: ["videoList", videoId],
    queryFn: ({ pageParam }) =>
      youtube.getVideoList(videoId, pageParam), // youtube api 요청 함수
    initialPageParam: "",
    getNextPageParam: (lastPage) => lastPage.nextPageToken || undefined,
  });

  useHandleScroll(fetchNextPage);
  1. initialPageParam: "" 으로 초기화한 뒤, queryFn의 첫 번째 요청 및 응답을 통해 다음 페이지에 대한 파라미터(nextPageToken)를 받아온다.
// api response 예시
{
  items: data,
    nextPageToken : 'abc',
    pageInfo: {
       totalResults: 200,
       resultsPerPage: 50,
    },
}
  1. 이 response를 tanstack-query에서 다음과 같은 형식으로 반환한다.
{
  pages: string[], 
  pageParams: object[],
}
// videoList 예시

// 1) 처음 fetch 후
{
  pages: [''], 
  pageParams: [
    {
      items: data,
      nextPageToken : 'abc',
      pageInfo: {
         totalResults: 200,
         resultsPerPage: 50,
      },
    }
  ]
}

// 2) 두 번째 fetch 후
{
  pages: ['', 'abc'], 
  pageParams: [
    {
      items: data,
      nextPageToken : 'abc',
      pageInfo: {
         totalResults: 200,
         resultsPerPage: 50,
      },
    },
    {
      items: data2,
      nextPageToken : 'def',
      pageInfo: {
         totalResults: 200,
         resultsPerPage: 50,
      },
    }
  ]
}
  1. getNextPageParam 함수에서 lastPage.nextPageToken이 있다면 fetchNextPage 함수를 통해 다음 api 통신을 다시 한다.

0개의 댓글