分页指南
示例
想跳过直接看实现?请查看这些示例:
- 分页
- 受控分页 (React Query)
- 可编辑数据
- 展开
- 过滤器
- 完全受控
- 行选择
分页指南
TanStack Table 对客户端和服务器端分页都有很好的支持。本指南将引导您了解在表格中实现分页的不同方式。
客户端分页
使用客户端分页意味着您获取的数据将包含表格的所有行,并且表格实例将在前端处理分页逻辑。
您应该使用客户端分页吗?
客户端分页通常是使用 TanStack Table 实现分页的最简单方法,但对于非常大的数据集来说可能不切实际。
然而,许多人低估了客户端可以处理的数据量。如果您的表格最多只有几千行或更少,客户端分页仍然是一个可行的选择。TanStack Table 旨在扩展到数万行,并在分页、过滤、排序和分组方面具有不错的性能。官方分页示例加载了 100,000 行,并且仍然表现良好,尽管只有少数几列。
每个用例都不同,将取决于表格的复杂性、您有多少列、每条数据的大小等等。需要注意的主要瓶颈是:
- 您的服务器能否在合理的时间(和成本)内查询所有数据?
- 获取的总大小是多少?(如果您没有很多列,这可能不会像您想象的那么糟糕。)
- 如果所有数据都一次性加载,客户端浏览器是否会占用过多内存?
如果您不确定,您可以随时从客户端分页开始,然后随着数据增长在未来切换到服务器端分页。
您应该改用虚拟化吗?
或者,您可以渲染大型数据集的所有行,但只使用浏览器的资源来渲染视口中可见的行。这种策略通常被称为“虚拟化”或“窗口化”。TanStack 提供了一个名为 TanStack Virtual 的虚拟化库,它可以很好地与 TanStack Table 配合使用。虚拟化和分页的 UI/UX 都有各自的权衡,所以请根据您的用例选择最适合您的方法。
分页行模型
如果您想利用 TanStack Table 中内置的客户端分页功能,您首先需要传入分页行模型。
import { useReactTable, getCoreRowModel, getPaginationRowModel } from '@tanstack/react-table';
//...
const table = useReactTable({
columns,
data,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(), // 加载客户端分页代码
});
手动服务器端分页
如果您决定需要使用服务器端分页,以下是实现方法。
服务器端分页不需要分页行模型,但是如果您在共享组件中为其他需要它的表格提供了它,您仍然可以通过将 manualPagination 选项设置为 true 来关闭客户端分页。将 manualPagination 选项设置为 true 会告诉表格实例在底层使用 table.getPrePaginationRowModel 行模型,并且它会使表格实例假定您传入的 data 已经分页。
页数和行数
表格实例将无法知道您的后端总共有多少行/页,除非您告诉它。提供 rowCount 或 pageCount 表格选项,让表格实例知道总共有多少页。如果您提供 rowCount,表格实例将根据 rowCount 和 pageSize 在内部计算 pageCount。否则,如果您已经有了 pageCount,可以直接提供它。如果您不知道页数,您可以将 pageCount 传入 -1,但在这种情况下,getCanNextPage 和 getCanPreviousPage 行模型函数将始终返回 true。
import { useReactTable, getCoreRowModel, getPaginationRowModel } from '@tanstack/react-table';
//...
const table = useReactTable({
columns,
data,
getCoreRowModel: getCoreRowModel(),
// getPaginationRowModel: getPaginationRowModel(), // 服务器端分页不需要
manualPagination: true, // 关闭客户端分页
rowCount: dataQuery.data?.rowCount, // 传入总行数,以便表格知道总共有多少页(如果未提供 pageCount,则在内部计算)
// pageCount: dataQuery.data?.pageCount, // 或者直接传入 pageCount 而不是 rowCount
});
注意:将 manualPagination 选项设置为 true 会使表格实例假定您传入的 data 已经分页。
分页状态
无论您是使用客户端分页还是手动服务器端分页,您都可以使用内置的 pagination 状态和 API。
pagination 状态是一个包含以下属性的对象:
pageIndex:当前页索引(从零开始)。pageSize:当前页大小。
您可以像管理表格实例中的任何其他状态一样管理 pagination 状态。
import { useReactTable, getCoreRowModel, getPaginationRowModel } from '@tanstack/react-table';
//...
const [pagination, setPagination] = useState({
pageIndex: 0, // 初始页索引
pageSize: 10, // 默认页大小
});
const table = useReactTable({
columns,
data,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
onPaginationChange: setPagination, // 当内部 API 改变分页状态时更新分页状态
state: {
//...
pagination,
},
});
或者,如果您不需要在自己的作用域中管理 pagination 状态,但需要为 pageIndex 和 pageSize 设置不同的初始值,您可以使用 initialState 选项。
const table = useReactTable({
columns,
data,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
initialState: {
pagination: {
pageIndex: 2, // 自定义初始页索引
pageSize: 25, // 自定义默认页大小
},
},
});
注意:不要同时将 pagination 状态传递给 state 和 initialState 选项。state 将覆盖 initialState。只能使用其中一个。
分页选项
除了对手动服务器端分页有用的 manualPagination、pageCount 和 rowCount 选项(上面已讨论)之外,还有一个其他表格选项值得理解。
自动重置页索引
默认情况下,当发生改变页面的状态更改时,例如数据更新、过滤器更改、分组更改等,pageIndex 会重置为 0。当 manualPagination 为 true 时,此行为会自动禁用,但可以通过将布尔值显式分配给 autoResetPageIndex 表格选项来覆盖。
const table = useReactTable({
columns,
data,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
autoResetPageIndex: false, // 关闭页索引的自动重置
});
但请注意,如果您关闭 autoResetPageIndex,您可能需要添加一些逻辑来自己处理重置 pageIndex,以避免显示空白页面。
分页 API
有几个分页表格实例 API 对连接您的分页 UI 组件很有用。
分页按钮 API
getCanPreviousPage:当在第一页时,用于禁用“上一页”按钮。getCanNextPage:当没有更多页面时,用于禁用“下一页”按钮。previousPage:用于转到上一页。(按钮点击处理程序)nextPage:用于转到下一页。(按钮点击处理程序)firstPage:用于转到第一页。(按钮点击处理程序)lastPage:用于转到最后一页。(按钮点击处理程序)setPageIndex:用于“转到页”输入。resetPageIndex:用于将表格状态重置为原始页索引。setPageSize:用于“页大小”输入/选择。resetPageSize:用于将表格状态重置为原始页大小。setPagination:用于一次性设置所有分页状态。resetPagination:用于将表格状态重置为原始分页状态。
注意:其中一些 API 是 v8.13.0 中的新功能。
<Button
onClick={() => table.firstPage()}
disabled={!table.getCanPreviousPage()}
>
{'<<'}
</Button>
<Button
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
>
{'<'}
</Button>
<Button
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
>
{'>'}
</Button>
<Button
onClick={() => table.lastPage()}
disabled={!table.getCanNextPage()}
>
{'>>'}
</Button>
<select
value={table.getState().pagination.pageSize}
onChange={e => {
table.setPageSize(Number(e.target.value))
}}
>
{[10, 20, 30, 40, 50].map(pageSize => (
<option key={pageSize} value={pageSize}>
{pageSize}
</option>
))}
</select>
分页信息 API
getPageCount:用于显示总页数。getRowCount:用于显示总行数。