useQuery()에서는 data에 한 페이지에 해당하는 데이터만 담고 있었지만, useInfiniteQuery()에서는 data에 모든 페이지의 데이터가 pages라는 프로퍼티로 배열에 담겨 있습니다.
import { useInfiniteQuery } from '@tanstack/react-query';
const {
data: postsData,
isPending,
isError,
hasNextPage,
fetchNextPage,
isFetchingNextPage,
} = useInfiniteQuery({
queryKey: ['posts'],
queryFn: ({ pageParam }) => getPosts(pageParam, PAGE_LIMIT),
initialPageParam: 0,
getNextPageParam: (lastPage, allPages, lastPageParam, allPageParams) =>
lastPage.hasMore ? lastPageParam + 1 : undefined,
});
✅ useInfiniteQuery() 훅에서는 page param 값을 활용하여 페이지를 더 불러옵니다.
✅ getNextPageParam() 함수에서 다음 페이지가 있는 경우 다음 page param 값을 리턴하는데요, fetchNextPage() 함수에서는 이렇게 리턴된 page param 값을 쿼리 함수로 전달해 다음 페이지의 데이터를 받아옵니다.
❗ 만약 getNextPageParams() 함수에서 undefined나 null 값을 리턴하면 다음 페이지가 없는 것으로 간주해 fetchNextPage() 함수를 실행해도 더 이상 데이터를 받아오지 않고, hasNextPage의 값도 false가 됩니다.
initialPageParam: 0, // 0 페이지가 초기값
getNextPageParam: (lastPage, allPages, lastPageParam, allPageParams) =>
lastPage.hasMore ? lastPageParam + 1 : undefined,
initialPageParam : 초기페이지 설정값
getNextPageParam : 다음 페이지의 설정값
lastPage : 현재까지 중 가장 마지막 페이지의 데이터가 전달
(만약 현재 2 페이지에 해당하는 데이터까지 받아 왔다면 2 페이지의 데이터가 lastPage로 전달)
allPages : 모든 페이지의 데이터
lastPageParam : 현재까지 중 가장 마지막 페이지의 설정값
(현재 2 페이지까지 받아 왔다면 lastPageParam은 2)
allPageParams : 모든 페이지의 각각의 페이지 설정값
getNextPageParam()에서는 파라미터로 받은 값들의 정보를 이용해 그 다음 페이지 값인 pageParam을 리턴해야 합니다.
이 프로젝트의 경우 현재 0 페이지라면 그다음 값은 1이 되어야 할 텐데요. 따라서 0 페이지의 데이터에서 hasMore 값을 확인 후, true인 경우 lastPageParam의 값인 0에 1을 더한 값 1을 리턴하도록 했습니다. false인 경우 undefined나 null을 리턴해 주면 되는데, 이는 다음 페이지가 없다는 것을 의미합니다.
이 pageParam 값은 쿼리 함수의 파라미터로 전달되므로, 이 값을 이용해 백엔드에 해당 페이지에 해당하는 데이터를 요청할 수 있습니다. 따라서 쿼리 함수를 아래처럼 수정할 수 있는 것이죠.
queryFn: ({ pageParam }) => getPosts(pageParam, PAGE_LIMIT)

🔼 pages 배열의 0번 인덱스에는 첫 번째 페이지(0 페이지)의 데이터가, pageParams 배열의 0번 인덱스에는 첫 번째 페이지값인 0이 들어있는 것을 확인할 수 있습니다.
fetchNextPage() 함수를 실행하면 getNextPageParam() 함수의 리턴 값이 undefined나 null이 아닌 경우, 해당 리턴 값을 쿼리 함수의 pageParam으로 전달해 그다음 페이지 데이터를 가져옵니다.
<div>
<button onClick={fetchNextPage}>더 불러오기</button>
</div>
더 이상 불러올 데이터가 없거나 다음 데이터를 불러오는 중일 때에는 "더 불러오기" 버튼을 비활성화하겠습니다.
useInfiniteQuery()의 리턴 값 중 hasNextPage와 isFetchingNextPage를 이용하면 다음과 같이 간단히 구현할 수 있습니다.
const {
data: postsData,
isPending,
isError,
hasNextPage,
fetchNextPage,
isFetchingNextPage,
} = useInfiniteQuery({
queryKey: ['posts'],
queryFn: ({ pageParam }) => getPosts(pageParam, PAGE_LIMIT),
initialPageParam: 0,
getNextPageParam: (lastPage, allPages, lastPageParam, allPageParams) =>
lastPage.hasMore ? lastPageParam + 1 : undefined,
});
// ...
return (
...
<button
onClick={fetchNextPage}
disabled={!hasNextPage || isFetchingNextPage}
>
더 불러오기
</button>
...
);