分页/滞后查询 Paginated Queries
呈现分页数据是一种非常常见的 UI 模式,在 React Query 中,它可以简单的通过在查询键中包含页面信息来"正常工作":
TypeScript
1const result = useQuery({2 queryKey: ["projects", page],3 queryFn: fetchProjects,4});但是,如果运行该示例,你可能会注意到一些奇怪的事情:
UI 在success和loading状态之间来回跳转,因为每个新页面都被视为一个全新的查询。
这种体验并不是最佳的,不幸的是,今天有不少工具在坚持使用。
但不是 React Query!
你可能已经猜到了,React Query 带有一个称为keepPreviousData的强大功能,从而使得这个问题可以被轻易的解决。
使用keepPreviousData的更好的分页查询
考虑下面的例子,在理想情况下,我们希望为查询增加 pageIndex(或游标)。
如果使用useQuery,从技术上讲它仍然可以正常工作,但是 UI 会随着每个页面或游标创建和销毁不同的查询而跳出 success 和 loading 状态。
通过将keepPreviousData设置为true,我们可以得到一些新的东西:
- 请求新数据时,即使查询键值已更改,上次成功获取的数据仍可用
- 当新数据到达时,先前的数据将被无缝交换以显示新数据
- 可以使用
isPreviousData来了解当前查询提供的是什么数据
TypeScript
1function Todos() {2 const [page, setPage] = React.useState(0);3 4 const fetchProjects = (page = 0) =>5 fetch("/api/projects?page=" + page).then((res) => res.json());6 7 const { isLoading, isError, error, data, isFetching, isPreviousData } =8 useQuery({9 queryKey: ["projects", page],10 queryFn: () => fetchProjects(page),11 keepPreviousData: true,12 });13 14 return (15 <div>16 {isLoading ? (17 <div>Loading...</div>18 ) : isError ? (19 <div>Error: {error.message}</div>20 ) : (21 <div>22 {data.projects.map((project) => (23 <p key={project.id}>{project.name}</p>24 ))}25 </div>26 )}27 <span>Current Page: {page + 1}</span>28 <button29 onClick={() => setPage((old) => Math.max(old - 1, 0))}30 disabled={page === 0}31 >32 Previous Page33 </button>{" "}34 <button35 onClick={() => {36 if (!isPreviousData && data.hasMore) {37 setPage((old) => old + 1);38 }39 }}40 // 禁用跳转下一页的按钮,直到我们知道下一页数据是可用的41 disabled={isPreviousData || !data?.hasMore}42 >43 Next Page44 </button>45 {isFetching ? <span> Loading...</span> : null}{" "}46 </div>47 );48}使用keepPreviousData滞后无限查询的结果
尽管不那么常见,但是keepPreviousData选项也可以与useInfiniteQueryHook 完美配合,所以你可以无缝地允许你的用户继续查看缓存的数据,而无限查询的键值将随着时间变化而自动变化。