본문 바로가기

온라인 강의(유데미, 인프런 등)/React-Query(유데미)

useInfiniteQuery

반응형

무한 스크롤

 

사용자가 스크롤 할 때마다 새로운 데이터를 가져오는 것이다. 한 번에 모든 데이터를 가져오는 것보다 훨씬 효율적이다. 그래서 사용자가 버튼을 클릭하거나 특정 지점을 스크롤 했을 때 데이터를 가져온다.

 

useInfiniteQuery

 

useInfiniteQuery는 페이지네이션과는 다른 API 포맷을 요구한다.

페이지네이션에서는 현재 페이지를 컴포넌트 상태에서 추적했었다. 사용자가 새 페이지를 열기 위해 행동을 하면 쿼리 키를 업데이트하고 쿼리 키가 데이터를 업데이트 하는 방식이였다.

반면에 useInfiniteQuery는 다음 쿼리가 무엇일지 추적한다. 이 경우에는 다음 쿼리가 데이터의 일부로 반환된다.

이 객체는 데이터 배열을 가진 결과라는 프로퍼티를 가진다(results). 또한, 데이터의 다음 페이지로 가려면 어떤 쿼리를 사용할지, 이전 페이지로 가려면 어떤 쿼리를 사용할지, 전체 개수를 제공한다.

 

useInfiniteQuery와 useQuery의 차이

 

useInfiniteQuery와 useQuery 는 반환되는 데이터의 형태에서 차이가 있다.

useQuery에서의 반환되는 데이터는 단순히 쿼리 함수에서 반환되는 데이터인 반면에, useInfiniteQuery에서의 반환 데이터 객체에는 두 개의 프로퍼티가 있다.

  1. pages : 모든 쿼리는 pages 배열에 각자의 고유한 요소가 있고 그 요소는 해당 쿼리에 대한 데이터이다. 페이지가 진행되면서 쿼리가 바뀐다.
  2. pageParams : 각 페이지의 파라미터가 담겨있다. 실제로 많이 사용되지는 않는다.

useInfiniteQuery의 syntax는 다음과 같다.

const {
	data,              
	hasNextPage, // 다음 페이지 유무
	fetchNextPage, // 다음 페이지를 가져오는 URL
	hasPreviousPage,
	fetchPreviosPage, 
	isLoading,
	isError,
	error,
	isFetching,
    isFetchingNextPage // 다음 페이지를 가져오는지, 일반적인 페칭인지 구별
} = useInfiniteQuery({
	queryKey: unknown[];
	quertFn: (paramPage) => void;
	getNextPageParam: (lastPage) => void; // 결과값 = hasNextPage
	getPreviousPageParam: (lastPage) => void; // 결과값 = hasPreviousPage
})

 

useInfiniteQuery의 Flow

 

1. Component mounts

이 시점에는 아직 쿼리를 만들지 않았기 때문에 useInfiniteQuery가 반환하는 data가 undefined이다.

2. Fetch first page

useInfiniteQuery는 pageParam을 아규먼트로 받는다.

첫 pageParam은 우리가 기본 값으로 정의한 것이 되고(defaultUrl)가 된다. 해당 pageParam을 사용해서 첫 번째 페이지를 가져오고 반환 객체 데이터의 페이지 프로퍼티를 설정하고 이게 쿼리 함수가 반환하는 값이 된다.

data.pages[0] : { 첫 번째 페이지 데이터 }

3. getNextPageParam

데이터가 반환된 후 리액트 쿼리는 getNextPageParam을 실행하여 pageParam을 업데이트 한다. 

pageParam : "http://~~~//?page=2"

이 시점에는 hasNextPage가 true이다. hasNexPage의 값은 pageParam이 정의되어 있는지 아닌지(undefined 인지 아닌지)에 따른다.

4. 유저가 스크롤 또는 다음 버튼 클릭

리액트 쿼리가 다음 페이지를 fetch 한다.

data.pages[1] : { 두 번째 페이지 데이터 }

이제 새 데이터를 가졌으니, 다시 3번에서의 getNextPageParam 을 실행한다.
만약 총 페이지가 2개라면 pageParam은 undefined가 되고, hasNextPage가 false가 된다.

 

예시 코드는 다음과 같다.

import InfiniteScroll from 'react-infinite-scroller';
import { useInfiniteQuery } from 'react-query';
import { Person } from './Person';

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

export function InfinitePeople() {
  // TODO: get data for InfiniteScroll via React Query
  const {
    data,
    fetchNextPage,
    hasNextPage,
    isLoading,
    isError,
    error,
    isFetching,
  } = useInfiniteQuery(
    'sw-people',
    ({ pageParam = initialUrl }) => fetchUrl(pageParam),
    {
      getNextPageParam: (lastPage) => lastPage.next || undefined,
      // 이 함수가 undefined를 반환하는지 여부에 따라 hasNextPage가 결정됨
      // lastPage.next가 null 이면(이전 페이지가 없으면) undefined를 반환하도록
    }
  );

 

React Infinite Scroller

 

이 패키지는 useInfiniteQuery와 잘 맞는다. React Infinite Scroller는 스스로 페이지의 끝에 도달했음을 인식하고 fetchNextPage를 불러오는 기능을 한다.

React Infinite Scroller에는 두 개의 프로퍼티가 있다.

  1. loadMore = { fetchNextPage } : 데이터가 더 필요할 때 불러와 useInfiniteQuery의 fetchNextPage 함숫값을 이용한다.
  2. hasMore = { hasNextPage } : useInfiniteQuery의 hasNextPage를 사용한다.

예시 코드는 다음과 같다.

return (
    <>
      {isFetching && <div className="loading">Loading...</div>}
      <InfiniteScroll loadMore={fetchNextPage} hasMore={hasNextPage}>
        {data.pages.map((pageData) => {
          return pageData.results.map((person) => {
            return (
              <Person
                key={person.name}
                name={person.name}
                hairColor={person.hair_color}
                eyeColor={person.eye_color}
              />
            );
          });
        })}
      </InfiniteScroll>
    </>
  );

 

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

 

react-infinite-scroller

Infinite scroll component for React in ES6. Latest version: 1.2.6, last published: a year ago. Start using react-infinite-scroller in your project by running `npm i react-infinite-scroller`. There are 488 other projects in the npm registry using react-infi

www.npmjs.com

 

useInfiniteQuery 페칭(Fetching)과 에러 상태

 
import InfiniteScroll from 'react-infinite-scroller';
import { useInfiniteQuery } from 'react-query';
import { Person } from './Person';

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

export function InfinitePeople() {
  // TODO: get data for InfiniteScroll via React Query
  const {
    data,
    fetchNextPage,
    hasNextPage,
    isLoading,
    isError,
    error,
    isFetching,
  } = useInfiniteQuery(
    'sw-people', 
    ({ pageParam = initialUrl }) => fetchUrl(pageParam), 
    {
      getNextPageParam: (lastPage) => lastPage.next || undefined,
 
    }
  );

  // 얼리 리턴
  // isFetching으로 얼리 리턴을 주게 되면
  // 새로운 페이지를 열어야 할 때 조기반환이 실행되기 때문에 스크롤이 다시 위로 올라감
  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((person) => {
            return (
              <Person
                key={person.name}
                name={person.name}
                hairColor={person.hair_color}
                eyeColor={person.eye_color}
              />
            );
          });
        })}
      </InfiniteScroll>
    </>
  );
}

 

양방향 스크롤(Bi-directional Scrolling)

 

양방향 스크롤은 데이터의 중간부터 시작할 때 유용하다. 그러면 시작점 이후 뿐 아니라 이전의 데이터도 필요하게 된다. 모든 next 메서드와 프로퍼티가 제공하는 것은 previous 방향에서도 제공된다.

반응형