useInfiniteQuery는 목록 데이터를 점진적으로 불러와 ‘더 보기’ 또는 ‘무한 스크롤’을 구현할 때 사용되는 훅이다. useQuery와 유사하지만, 여러 페이지의 데이터를 효율적으로 관리할 수 있도록 추가 기능을 제공한다.
const {
data,
error,
fetchNextPage,
hasNextPage,
isFetching,
isFetchingNextPage,
status,
} = useInfiniteQuery({
queryKey: ['projects'],
queryFn: fetchProjects,
initialPageParam: 0,
getNextPageParam: (lastPage, pages) => lastPage.nextCursor,
})
데이터 구조 변화
data.pages: 불러온 모든 페이지 데이터를 포함하는 배열
data.pageParams: 각 페이지를 가져오는 데 사용된 매개변수를 포함하는 배열
추가 메서드
fetchNextPage: 다음 페이지 데이터를 불러오는 함수
fetchPreviousPage: 이전 페이지 데이터를 불러오는 함수
추가 옵션
initialPageParam: 초기 페이지 매개변수 (필수)
getNextPageParam: 다음 페이지를 불러올 수 있는 기준 정의
getPreviousPageParam: 이전 페이지를 불러올 수 있는 기준 정의
추가 상태값
hasNextPage: 다음 페이지가 존재하는지 여부 (getNextPageParam이 null/undefined가 아니면 true)
hasPreviousPage: 이전 페이지가 존재하는지 여부 (getPreviousPageParam이 null/undefined가 아니면 true)
isFetchingNextPage: 다음 페이지 데이터를 요청 중인지 여부
isFetchingPreviousPage: 이전 페이지 데이터를 요청 중인지 여부
getNextPageParam
getNextPageParam을 보면 lastPage.nextPage값을 통해 다음 페이지가 있는지 설정한다.
{"items": [...], "nextPage": 3 }
서버에서 이런 형식의 데이터를 받았을 때의 상황이다.
만약 totalCount 정도만 받는다면 직접 계산을 해서 getNextPageParam에 넣으면 된다.
pageParam
useInfiniteQuery에서 queryFn의 인자로 pageParam를 자동으로 전달해준다.
<script setup>
import { useInfiniteQuery } from '@tanstack/vue-query'
const fetchPosts = async ({ pageParam = 0, limit = 10 }) => {
const res = await fetch(`/api/posts?page=${pageParam}&limit=${limit}`)
return res.json()
}
const {
data,
fetchNextPage,
hasNextPage,
isFetchingNextPage,
} = useInfiniteQuery({
queryKey: ['posts'],
queryFn: fetchPosts,
initialPageParam: 1, // 초기 페이지
getNextPageParam: (lastPage) => lastPage.nextPage, // 다음 페이지 번호 반환
})
</script>
<template>
<div>
<Post v-for="post in data?.pages.flatMap((group) => group.items)" :key="post.id" :post="post" />
<button v-if="hasNextPage" @click="fetchNextPage" :disabled="isFetchingNextPage">
{{ isFetchingNextPage ? '로딩 중...' : '더 보기' }}
</button>
</div>
</template>