分页指南

示例

想要直接跳到实现部分吗?请查看以下示例:

API

分页 API

分页指南

TanStack Table 对客户端分页和服务器端分页都提供了很好的支持。本指南将引导您了解在表格中实现分页的不同方法。

客户端分页

使用客户端分页意味着您获取的 data 将包含表格的所有行,并且表格实例将在前端处理分页逻辑。

您应该使用客户端分页吗?

在使用 TanStack Table 时,客户端分页通常是实现分页的最简单方法,但对于非常大的数据集,它可能不切实际。

然而,许多人低估了客户端可以处理多少数据。如果您的表格最多只有几千行,那么客户端分页仍然是一个可行的选择。TanStack Table 旨在扩展到数万行,并在分页、筛选、排序和分组方面具有不错的性能。官方分页示例加载了 100,000 行,并且仍然表现良好,尽管只有少数几列。

每个用例都不同,具体取决于表格的复杂性、列数、每个数据片段的大小等。需要注意的主要瓶颈是:

  1. 您的服务器能否在合理的时间(和成本)内查询所有数据?
  2. 获取的总大小是多少?(如果您没有很多列,这可能不会像您想象的那么糟糕。)
  3. 如果一次加载所有数据,客户端浏览器是否会使用过多的内存?

如果您不确定,可以先从客户端分页开始,然后在数据增长时切换到服务器端分页。

您应该改用虚拟化吗?

或者,您可以将大型数据集的所有行呈现在同一页面上,而不是对数据进行分页,但仅使用浏览器资源来呈现视口中可见的行。这种策略通常称为“虚拟化”或“窗口化”。TanStack 提供了一个名为 TanStack Virtual 的虚拟化库,它可以与 TanStack Table 很好地配合使用。虚拟化和分页的 UI/UX 都有其自身的权衡,因此请查看哪种方法最适合您的用例。

分页行模型

如果您想利用 TanStack Table 中内置的客户端分页,首先需要传入分页行模型。

jsx
import { useReactTable, getCoreRowModel, getPaginationRowModel } from '@tanstack/react-table';
//...
const table = useReactTable({
  columns,
  data,
  getCoreRowModel: getCoreRowModel(),
  getPaginationRowModel: getPaginationRowModel(), //加载客户端分页代码
});
import { useReactTable, getCoreRowModel, getPaginationRowModel } from '@tanstack/react-table';
//...
const table = useReactTable({
  columns,
  data,
  getCoreRowModel: getCoreRowModel(),
  getPaginationRowModel: getPaginationRowModel(), //加载客户端分页代码
});

手动服务器端分页

如果您决定需要使用服务器端分页,可以按以下步骤实现。

服务器端分页不需要分页行模型,但是如果您已为其他需要它的表格在共享组件中提供了该模型,则仍可以通过将 manualPagination 选项设置为 true 来关闭客户端分页。将 manualPagination 选项设置为 true 将告诉表格实例在后台使用 table.getPrePaginationRowModel 行模型,并且它将使表格实例假定您传入的 data 已分页。

页面计数和行计数

除非您告知表格实例,否则它无法知道后端总共有多少行/页。提供 rowCountpageCount 表格选项以告知表格实例总共有多少页。如果您提供 rowCount,表格实例将根据 rowCountpageSize 在内部计算 pageCount。或者,如果您已经有了 pageCount,则可以直接提供它。如果您不知道页面计数,则可以为 pageCount 传入 -1,但在这种情况下,getCanNextPagegetCanPreviousPage 行模型函数将始终返回 true

jsx
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
});
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 状态。

jsx
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,
  },
});
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 状态,但需要为 pageIndexpageSize 设置不同的初始值,则可以使用 initialState 选项。

jsx
const table = useReactTable({
  columns,
  data,
  getCoreRowModel: getCoreRowModel(),
  getPaginationRowModel: getPaginationRowModel(),
  initialState: {
    pagination: {
      pageIndex: 2, //自定义初始页面索引
      pageSize: 25, //自定义默认页面大小
    },
  },
});
const table = useReactTable({
  columns,
  data,
  getCoreRowModel: getCoreRowModel(),
  getPaginationRowModel: getPaginationRowModel(),
  initialState: {
    pagination: {
      pageIndex: 2, //自定义初始页面索引
      pageSize: 25, //自定义默认页面大小
    },
  },
});

注意:不要同时将 pagination 状态传递给 stateinitialState 选项。state 将覆盖 initialState。仅使用其中一个。

分页选项

除了对手动服务器端分页有用的 manualPaginationpageCountrowCount 选项(在上文中已讨论)之外,还有一个需要理解的表格选项。

自动重置页面索引

默认情况下,当影响分页的状态更改(例如更新 data、更改筛选器、更改分组等)时,pageIndex 会重置为 0。当 manualPagination 为 true 时,此行为会自动禁用,但可以通过显式地为 autoResetPageIndex 表格选项分配布尔值来覆盖此行为。

jsx
const table = useReactTable({
  columns,
  data,
  getCoreRowModel: getCoreRowModel(),
  getPaginationRowModel: getPaginationRowModel(),
  autoResetPageIndex: false, //关闭 pageIndex 的自动重置
});
const table = useReactTable({
  columns,
  data,
  getCoreRowModel: getCoreRowModel(),
  getPaginationRowModel: getPaginationRowModel(),
  autoResetPageIndex: false, //关闭 pageIndex 的自动重置
});

但是请注意,如果关闭 autoResetPageIndex,您可能需要添加一些逻辑来自己处理重置 pageIndex,以避免显示空页面。

分页 API

有几个分页表格实例 API 可用于连接您的分页 UI 组件。

分页按钮 API

  • getCanPreviousPage:用于在第一页时禁用“上一页”按钮。
  • getCanNextPage:用于在没有更多页面时禁用“下一页”按钮。
  • previousPage:用于转到上一页。(按钮单击处理程序)
  • nextPage:用于转到下一页。(按钮单击处理程序)
  • firstPage:用于转到第一页。(按钮单击处理程��)
  • lastPage:用于转到最后一页。(按钮单击处理程序)
  • setPageIndex:用于“转到页面”输入。
  • resetPageIndex:用于将表格状态重置为原始页面索引。
  • setPageSize:用于“页面大小”输入/选择。
  • resetPageSize:用于将表格状态重置为原始页面大小。
  • setPagination:用于一次设置所有分页状态。
  • resetPagination:用于将表格状态重置为原始分页状态。

注意:其中一些 API 是 v8.13.0 中的新增功能。

jsx
<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>
<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:用于显示总行数。