前端
PythonJava运维数据库
TanStack Query
TanStack Query
  • getting started
    • 快速开始
    • 开发工具
    • Typescript
    • 默认配置
    • 网络模式
  • Query
    • 查询
    • 查询键
    • 查询函数
    • 查询选项
    • 并行查询
    • 有依赖的查询
    • 查询状态指示器
    • 窗口聚焦重新获取数据
    • 禁用/暂停查询
    • 查询重试
    • 分页查询
    • 无限查询
    • 初始化查询数据
  • 占位符
  • Mutation
    • 修改 Mutations
    • 主动查询失效 Query Invalidation
    • 修改导致的失效 Invalidation From Mutations
    • 通过修改的数据更新查询内容
    • 乐观更新
  • 取消查询
  • 默认查询函数
  • 过滤器
  • Suspense
  • 缓存数据
  • Api Reference
    • QueryClient
    • QueryCache
    • MutationCache
    • QueryObserver QueriesObserver
    • InfiniteQueryObserver
    • FocusManager
    • NotifyManager
    • useQuery
    • useQueries useInfiniteQuery
    • useMutation
    • useIsFetching
    • useIsMutating
    • useMutationState
    • useSuspenseQuery
    • useSuspenseInfiniteQuery
    • useSuspenseQueries
    • QueryClientProvider
    • useQueryClient
    • queryOptions
    • infiniteQueryOptions
    • QueryErrorResetBoundary
    • useQueryErrorResetBoundary
    • hydration
由 GitBook 提供支持
在本页
  • 使用 useInfiniteQuery 查询
  • 关键点:
  • 处理同时进行的请求:
  • 无限查询的重取操作:
  • 双向无限列表
  • 翻转页面顺序:
  • 限制存储的页面数量:
  • 没有游标的 API:

这有帮助吗?

  1. Query

无限查询

在 TanStack Query 中,实现加载更多数据或无限滚动的功能是一种常见的 UI 模式。为此,useInfiniteQuery 钩子可以用于查询这类数据列表,并提供了许多有用的特性来处理分页和数据加载。

使用 useInfiniteQuery 查询

与 useQuery 不同,useInfiniteQuery 返回的数据是一个对象,其中包含以下内容:

  • data.pages:存储所有已获取的页面数据。

  • data.pageParams:存储用于获取各页面的参数。

  • fetchNextPage 和 fetchPreviousPage:分别用于获取下一页和上一页数据。

  • getNextPageParam 和 getPreviousPageParam:这两个选项用于判断是否还有更多数据需要加载,以及获取下一页或上一页的数据。

示例:基本的无限查询

假设我们的 API 返回每页 3 个项目,并使用一个游标来获取下一组数据:

import { useInfiniteQuery } from "@tanstack/react-query";

function Projects() {
  const fetchProjects = async ({ pageParam }) => {
    const res = await fetch("/api/projects?cursor=" + pageParam);
    return res.json();
  };

  const { data, error, fetchNextPage, hasNextPage, isFetching, isFetchingNextPage, status } = useInfiniteQuery({
    queryKey: ["projects"],
    queryFn: fetchProjects,
    initialPageParam: 0, // 初始页面参数
    getNextPageParam: (lastPage) => lastPage.nextCursor // 获取下一页的参数
  });

  return status === "pending" ? (
    <p>Loading...</p>
  ) : status === "error" ? (
    <p>Error: {error.message}</p>
  ) : (
    <>
      {data.pages.map((group, i) => (
        <React.Fragment key={i}>
          {group.data.map((project) => (
            <p key={project.id}>{project.name}</p>
          ))}
        </React.Fragment>
      ))}
      <div>
        <button onClick={() => fetchNextPage()} disabled={!hasNextPage || isFetchingNextPage}>
          {isFetchingNextPage ? "Loading more..." : hasNextPage ? "Load More" : "Nothing more to load"}
        </button>
      </div>
      <div>{isFetching && !isFetchingNextPage ? "Fetching..." : null}</div>
    </>
  );
}

关键点:

  • data.pages:存储所有已获取的数据页。

  • fetchNextPage:用于请求下一页数据。

  • hasNextPage:如果返回值不是 null 或 undefined,则表示有下一页数据。

  • isFetchingNextPage:标识是否正在加载下一页的数据。

处理同时进行的请求:

在发起 fetchNextPage 请求时,如果有正在进行的请求(例如刷新数据),可能会导致数据覆盖。为了避免这种情况,您可以使用 { cancelRefetch: false } 选项来允许同时发起多个请求,但如果不是必需,最好避免在正在请求数据时发起其他请求。

<List onEndReached={() => !isFetchingNextPage && fetchNextPage()} />

无限查询的重取操作:

当一个无限查询变得过时并需要重新获取时,数据将按顺序重新请求,从第一页开始。这可以确保即使底层数据发生变化,我们也不会使用过时的游标,避免出现重复或漏掉记录的情况。如果查询缓存中的数据被移除,分页将重新开始,只有初始组会被请求

双向无限列表

如果您想实现一个双向无限列表,可以使用 getPreviousPageParam、fetchPreviousPage、hasPreviousPage 和 isFetchingPreviousPage 属性和函数

useInfiniteQuery({
  queryKey: ["projects"],
  queryFn: fetchProjects,
  initialPageParam: 0,
  getNextPageParam: (lastPage) => lastPage.nextCursor,
  getPreviousPageParam: (firstPage) => firstPage.prevCursor
});

翻转页面顺序:

如果您希望以相反的顺序显示页面数据,可以使用 select 选项:

useInfiniteQuery({
  queryKey: ["projects"],
  queryFn: fetchProjects,
  select: (data) => ({
    pages: [...data.pages].reverse(),
    pageParams: [...data.pageParams].reverse()
  })
});

手动更新无限查询:

您可以通过以下方法手动更新无限查询的数据:

  • 删除第一页:

queryClient.setQueryData(["projects"], (data) => ({
  pages: data.pages.slice(1),
  pageParams: data.pageParams.slice(1)
}));
  • 从单个页面中删除特定值:

const newPagesArray = oldPagesArray?.pages.map((page) => page.filter((val) => val.id !== updatedId)) ?? [];

queryClient.setQueryData(["projects"], (data) => ({
  pages: newPagesArray,
  pageParams: data.pageParams
}));

限制存储的页面数量:

如果您希望在查询数据中限制存储的页面数量,可以使用 maxPages 选项:

useInfiniteQuery({
  queryKey: ["projects"],
  queryFn: fetchProjects,
  initialPageParam: 0,
  getNextPageParam: (lastPage) => lastPage.nextCursor,
  getPreviousPageParam: (firstPage) => firstPage.prevCursor,
  maxPages: 3 // 只保存最多3页数据
});

没有游标的 API:

如果您的 API 不返回游标,可以将 pageParam 用作游标,通过 getNextPageParam 和 getPreviousPageParam 计算下一个或上一个页面的参数:

return useInfiniteQuery({
  queryKey: ["projects"],
  queryFn: fetchProjects,
  initialPageParam: 0,
  getNextPageParam: (lastPage, allPages, lastPageParam) => {
    if (lastPage.length === 0) {
      return undefined;
    }
    return lastPageParam + 1;
  },
  getPreviousPageParam: (firstPage, allPages, firstPageParam) => {
    if (firstPageParam <= 1) {
      return undefined;
    }
    return firstPageParam - 1;
  }
});

通过这些特性,您可以根据需求灵活地实现分页、无限滚动以及双向加载的功能。

上一页分页查询下一页初始化查询数据

最后更新于2个月前

这有帮助吗?