框架
版本

分页 / 延迟查询

渲染分页数据是一种非常常见的 UI 模式,在 TanStack Query 中,通过在查询键中包含页面信息,它“就是这样工作”的

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

但是,如果你运行这个简单的例子,你可能会注意到一些奇怪的事情

UI 在 成功待定 状态之间来回跳转,因为每一页都被视为一个全新的查询。

这种体验不是最优的,而且不幸的是,许多现有工具都坚持这样工作。但 TanStack Query 不是!正如你可能猜到的,TanStack Query 有一个很棒的功能叫做 placeholderData,它允许我们绕过这个问题。

使用placeholderData更好地进行分页查询

考虑以下示例,我们理想情况下希望递增查询的 pageIndex(或光标)。如果我们使用 useQuery,**它仍然可以正常工作**,但 UI 会在 成功待定 状态之间跳转,因为每次分页或光标更改都会创建和销毁不同的查询。通过将 placeholderData 设置为 (previousData) => previousData 或从 TanStack Query 导出的 keepPreviousData 函数,我们可以获得一些新的东西。

  • 即使查询键已更改,在请求新数据时,仍可获取上次成功获取的数据.
  • 当新数据到达时,之前的数据会无缝地替换为显示新数据。
  • isPlaceholderData 可用于了解查询当前为您提供的数据
tsx
import { keepPreviousData, useQuery } from '@tanstack/solid-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>
  )
}
import { keepPreviousData, useQuery } from '@tanstack/solid-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延迟无限查询结果

虽然不那么常见,但 placeholderData 选项也与 useInfiniteQuery hook 完美配合,因此你可以无缝地让用户在无限查询键随着时间变化时继续看到缓存数据。