본문으로 건너뛰기

페이지가 매겨진 쿼리 / 지연된 쿼리

페이지네이션된 데이터를 렌더링하는 것은 매우 일반적인 UI 패턴이며, TanStack Query에서는 페이지 정보를 쿼리 키에 포함하기만 하면 "바로 작동합니다":

const result = useQuery({
queryKey: ['projects', page],
queryFn: () => fetchProjects(page),
})

하지만 이 간단한 예제를 실행하면 이상한 점을 발견할 수 있습니다:

새 페이지마다 완전히 새로운 쿼리로 처리되므로 UI가 successpending 상태에 진입했다가 벗어납니다.

이러한 사용 경험은 최적이라고 할 수 없으며, 안타깝게도 오늘날 많은 도구가 고집하는 작동 방식입니다. 하지만 TanStack Query는 그렇지 않습니다! 짐작하셨겠지만 TanStack Query에는 이를 피할 수 있게 해주는 placeholderData이라는 훌륭한 기능이 함께 제공됩니다.

placeholderData를 사용한 더 나은 페이지네이션 쿼리

쿼리의 pageIndex(또는 커서)를 증가시키는 것이 이상적인 다음 예시를 살펴보세요. useQuery를 사용해도 기술적으로는 여전히 정상적으로 작동하지만, 각 페이지 또는 커서마다 서로 다른 쿼리가 생성되고 제거되면서 UI가 successpending 상태를 오가게 됩니다. placeholderData(previousData) => previousData 또는 TanStack Query에서 내보낸 keepPreviousData 함수로 설정하면 몇 가지 새로운 이점을 얻습니다:

  • 쿼리 키가 변경되었더라도 새 데이터를 요청하는 동안 마지막으로 성공한 가져오기의 데이터를 사용할 수 있습니다.
  • 새 데이터가 도착하면 이전 data가 자연스럽게 교체되어 새 데이터를 표시합니다.
  • isPlaceholderData를 통해 쿼리가 현재 어떤 데이터를 제공하는지 알 수 있습니다.
import { keepPreviousData, useQuery } from '@tanstack/react-query'
import React from 'react'

function Todos() {
const [page, setPage] = React.useState(0)

const fetchProjects = (page = 0) =>
fetch('/api/projects?page=' + page).then((res) => res.json())

const { isPending, isError, error, data, isFetching, isPlaceholderData } =
useQuery({
queryKey: ['projects', page],
queryFn: () => fetchProjects(page),
placeholderData: keepPreviousData,
})

return (
<div>
{isPending ? (
<div>Loading...</div>
) : isError ? (
<div>Error: {error.message}</div>
) : (
<div>
{data.projects.map((project) => (
<p key={project.id}>{project.name}</p>
))}
</div>
)}
<span>Current Page: {page + 1}</span>
<button
onClick={() => setPage((old) => Math.max(old - 1, 0))}
disabled={page === 0}
>
Previous Page
</button>
<button
onClick={() => {
if (!isPlaceholderData && data.hasMore) {
setPage((old) => old + 1)
}
}}
// Disable the Next Page button until we know a next page is available
disabled={isPlaceholderData || !data?.hasMore}
>
Next Page
</button>
{isFetching ? <span> Loading...</span> : null}
</div>
)
}

placeholderData를 사용해 Infinite Query 결과 지연하기

흔한 경우는 아니지만 placeholderData 옵션은 useInfiniteQuery 훅과도 완벽하게 작동하므로, 무한 쿼리 키가 시간에 따라 변경되는 동안에도 사용자가 캐시된 데이터를 계속 볼 수 있도록 원활하게 지원할 수 있습니다.