그럼에도 불구하고

👨‍💻

[React Query] useInfiniteQuery와 Infinite Scroll 본문

React/React query

[React Query] useInfiniteQuery와 Infinite Scroll

zenghyun 2023. 8. 30. 17:47

useInfiniteQuery와 Infinite Scroll에 대해 알아보겠습니다.

 

 

🧑🏻‍💻 useInfiniteQuery

기존 데이터를 모두 불러왔을 때, 더 많은 데이터를 아래에 불러오는 "무한 스크롤"은 매우 흔한 UI 패턴입니다.

Tanstack Query는 이런 리스트형 데이터를 요청하기 위해, useQuery의 유용한 버전인 useInfiniteQuery를 지원합니다. 

 

즉, useInfiniteQuery는 파라미터 값만 변경하여 동일한 useQuery를 무한정 호출할 때 사용합니다. 

 

useInfiniteQuery는 전반적으로 useQuery와 동일하지만, useInfiniteQuery와 useQuery의 차이를 비교하며 useInfiniteQuery의 기능을 살펴보겠습니다. 

 

📌 반환 객체에서 반환된 데이터 프로퍼티의 형태 차이

useQuery에서의 데이터는 단순히 쿼리함수에서 반환된 데이터를 말합니다. 

 

반면, useInfiniteQuery는 두 개의 프로퍼티를 가지고 옵니다.

 

첫 번째는 데이터 페이지 객체의 Array 페이지이고, 이때 각 페이지에 있는 각 요소가 바로 useQuery를 통해 받아오는 데이터들입니다.

 

두 번째는 각 페이지의 매개변수가 기록되는 pageParam입니다. pageParam는 검색된 쿼리의 키를 추적하기 때문입니다. 모든 쿼리는 페이지 배열에 고유한 요소를 가지고 있고, 그 요소는 해당 쿼리에 대한 데이터에 해당합니다. 따라서 페이지가 진행되면서 쿼리도 변하게 됩니다. 

 

⭐️  pageParam은 useInfiniteQuery가 현재 어떤 페이지에 있는지 확인할 수 있는 파라미터 값입니다.

 

📌 useInfiniteQuery의 옵션

  • ⭐️ getNextPageParam
    • 다음 페이지로 가는 방식을 정의하는 함수입니다.
    • 마지막 또는 모든 페이지에 대한 데이터를 다룹니다. 
    • 다음 api를 요청할 때 사용될 pageParam값을 정할 수 있습니다.
  • ⭐️ fetchNextPage
    • 사용자가 더 많은 데이터를 요청할 때 호출하는 함수입니다. 
  • ⭐️ hasNextPage
    • getNextPageParam의 반환 값을 기반으로 하는 함수입니다.
    • 마지막 쿼리의 데이터를 어떻게 사용할지 지시합니다.
    • undefined의 경우 더 이상 데이터가 없다는 뜻으로 가져올 데이터가 없음을 나타냅니다.
  • ⭐️ isFetchingNextPage
    • 다음 페이지를 가져오는 건지, 일반적인 fetching인지를 구별합니다.

 

https://tanstack.com/query/v4/docs/react/reference/useInfiniteQuery

 

useInfiniteQuery | TanStack Query Docs

const { fetchNextPage,

tanstack.com

 

🔄 흐름도 (Flow)

1. ⭐️ Component Mount 

 

이 시점에서는 useInfiniteQuery이 반환된 객체의 data 프로퍼티가 아직 정의되어 있지 않습니다. ( 아직 Query가 만들어지지 않았기 때문)

const { data ... } = useInfiniteQuery( ... ); 

👉 data : undefined

 

2. ⭐️ Fetch first page

 

useInfiniteQuery은 쿼리 함수를 사용하여 첫 페이지를 가져옵니다.

이때 쿼리함수는 useInfiniteQuery 번째 인수이고, pageParam 인수로 받습니다

const { data, ... } = useInfinteQuery([key], { pageParam = initialUrl } => function(pageParam), ... )

👉 data.pages[0] : {...} // 첫 번째 페이지 
👉 pageParam : initialUrl (default)

 

3. ⭐️ getNextPageParam update pageParam

 

데이터가 반환된 React Query getNextPageParam 실행하고, getNextPageParam pageParam 업데이트합니다.

// 이때는 API에서 반환된 데이터양식에 따라 lastPage, Allpage를 고르면 됩니다.
getNextPageParam: (lastPage, allPage) => ...

 const { data } = useInfiniteQuery(
    [key],
    ({ pageParam = initialUrl }) => function(pageParam),
    {
      getNextPageParam: (lastPage) => ...
    }
  );

4. ⭐️ hasNextPage? 

 

React Query가 hasNextPage의 값을 결정하는 방식은 pageParam이 정의되어 있는지 아닌지에 따릅니다. 즉, 다음으로 올 페이지의 유무에 따라 함수가 사용됩니다.

 

5. ⭐️ yes -> (use scrolls or clicks button) fetchNextPage

 

사용자가 스크롤하거나 버튼을 클릭하는 등의 fetchNextPage를 트리거할 만한 행동을 했다면 다음 요소를 업데이트하거나 페이지 배열에 다음 요소를 추가합니다.

그 후에 다시 3번으로 돌아가서 같은 작업을 실행합니다.

 

6. ⭐️ no -> done

 

fetchNextPage를 트리거 할 만한 행동이 없다면 이대로 종료합니다.

 

 

🧑🏻‍💻 React Infinite Scroller 

이 패키지는 창과 스크롤 가능한 요소를 모두 지원하여 간단하고 가벼운 무한 스크롤 페이지 또는 요소를 만들 수 있게 해 줍니다. 무엇보다 useInfiniteQuery와 호환이 잘 됩니다. 

 

📌 loadMore : 데이터가 더 필요할 때 불러와 useInfiniteQuery의 fetchNextPage 함숫값을 이용합니다.

📌 hasMore : hasNextPage와 같이 useInfiniteQuery에서 나온 객체를 해체한 값을 이용합니다.

 

 

예시 코드

import InfiniteScroll from "react-infinite-scroller";
import { useInfiniteQuery } from "@tanstack/react-query";
import { Species } from "./Species";

const initialUrl = "https://swapi.dev/api/species/";
const fetchUrl = async (url) => {
  const response = await fetch(url);
  return response.json();
};

export function InfiniteSpecies() {
  const {
    data,
    fetchNextPage,
    hasNextPage,
    isLoading,
    isFetching,
    isError,
    error,
  } = useInfiniteQuery(
    ["sw-species"],
    ({ pageParam = initialUrl }) => fetchUrl(pageParam),
    {
      getNextPageParam: (lastPage) => lastPage.next || undefined,
    }
  );

  if(isLoading) {
    return <div className="loading">Loading...</div>;
  }
  if(isError) {
    return <div>Error! {error.toString()}</div>;
  }
  return (
    <>
    {isFetching && <div className="loading">Loading...</div>}
    <InfiniteScroll loadMore={fetchNextPage} hasMore={hasNextPage}>
      {data.pages.map((pageData) => {
        return pageData.results.map((species) => {
          return <Species
            key={species.name}
            name={species.name}
            language={species.language}
            averageLifespan={species.average_lifespan}
          />
        })
      })}
    </InfiniteScroll>
    </>
  );
}

여기서 무한 스크롤 컴포넌트는 스스로 페이지의 끝에 도달했음을 인식하고 fetchNextPage를 불러오는 기능을 수행합니다.  😊

 

🏷️ 출처

https://jforj.tistory.com/246

https://velog.io/@pjh1011409/React-Query-Prefetching-Pagination

https://www.npmjs.com/package/react-infinite-scroller

https://velog.io/@cnsrn1874/react-query-useInfiniteQuery

 

 

 

 

Comments