무한 스크롤
사용자가 스크롤 할 때마다 새로운 데이터를 가져오는 것이다. 한 번에 모든 데이터를 가져오는 것보다 훨씬 효율적이다. 그래서 사용자가 버튼을 클릭하거나 특정 지점을 스크롤 했을 때 데이터를 가져온다.
useInfiniteQuery
useInfiniteQuery는 페이지네이션과는 다른 API 포맷을 요구한다.
페이지네이션에서는 현재 페이지를 컴포넌트 상태에서 추적했었다. 사용자가 새 페이지를 열기 위해 행동을 하면 쿼리 키를 업데이트하고 쿼리 키가 데이터를 업데이트 하는 방식이였다.
반면에 useInfiniteQuery는 다음 쿼리가 무엇일지 추적한다. 이 경우에는 다음 쿼리가 데이터의 일부로 반환된다.
이 객체는 데이터 배열을 가진 결과라는 프로퍼티를 가진다(results). 또한, 데이터의 다음 페이지로 가려면 어떤 쿼리를 사용할지, 이전 페이지로 가려면 어떤 쿼리를 사용할지, 전체 개수를 제공한다.
useInfiniteQuery와 useQuery의 차이
useInfiniteQuery와 useQuery 는 반환되는 데이터의 형태에서 차이가 있다.
useQuery에서의 반환되는 데이터는 단순히 쿼리 함수에서 반환되는 데이터인 반면에, useInfiniteQuery에서의 반환 데이터 객체에는 두 개의 프로퍼티가 있다.
- pages : 모든 쿼리는 pages 배열에 각자의 고유한 요소가 있고 그 요소는 해당 쿼리에 대한 데이터이다. 페이지가 진행되면서 쿼리가 바뀐다.
- 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에는 두 개의 프로퍼티가 있다.
- loadMore = { fetchNextPage } : 데이터가 더 필요할 때 불러와 useInfiniteQuery의 fetchNextPage 함숫값을 이용한다.
- 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
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 방향에서도 제공된다.
'온라인 강의(유데미, 인프런 등) > React-Query(유데미)' 카테고리의 다른 글
쿼리 특성 1 : Pre-fetching과 페이지네이션 (0) | 2023.09.01 |
---|---|
커스텀 쿼리 훅(관심사 분리) & 로딩 / 에러 처리 중앙집중화 (0) | 2023.08.31 |
쿼리 키 & 페이지네이션 & 데이터 pre-fetching & isLoading / isFetching & Mutation (0) | 2023.08.30 |
React Query 개발자 도구 & staleTime vs cacheTime (0) | 2023.08.30 |
React Query : React로 서버 상태 관리하기 (0) | 2023.08.30 |