TanStack Table PaginationState 详解:pageIndex 与 pageSize 的底层语义与实战配置
2026/9/21 16:43:46 网站建设 项目流程

TanStack Table PaginationState 详解:pageIndex 与 pageSize 的底层语义与实战配置

【免费下载链接】table🤖 Headless UI for building powerful tables & datagrids for TS/JS - React-Table, Vue-Table, Solid-Table, Svelte-Table项目地址: https://gitcode.com/gh_mirrors/ta/table

PaginationState是 TanStack Table(本仓库gh_mirrors/ta/table的 monorepo 核心packages/table-core)中行分页功能(row pagination)的状态契约。它由pageIndex(当前页码,从 0 开始)与pageSize(每页行数)两个字段构成,是全库所有分页 API、getPaginatedRowModel行模型切片以及手动/服务端分页模式的共同数据基础。读完本文,你将掌握PaginationState两个字段的精确语义、pageSize: Infinity的“单页全量”行为、状态在客户端与服务端分页中的流转方式,以及围绕它构建的导航、重置、受控状态等完整 API 用法。

接口定义与字段语义

PaginationState在 rowPaginationFeature.types.ts 中定义,只有两个必填字段:

export interface PaginationState { pageIndex: number /** * The number of rows per page. Set to `Infinity` to place all rows on a * single page. */ pageSize: number }

pageIndex:从 0 开始的当前页码

pageIndex表示当前展示的页码,从 0 开始计数。这意味着 UI 上通常展示的“第 1 页”对应pageIndex: 0,而示例中Page {table.state.pagination.pageIndex + 1} of {table.getPageCount()}的渲染方式正是对这种 0 基语义的显式换算(见 examples/react/pagination/src/main.tsx)。

该字段直接影响分页切片计算。在 createPaginatedRowModel.ts 中,分页行模型通过pageSize * pageIndex计算起始行、pageStart + pageSize计算结束行:

const { pageSize, pageIndex } = pagination ?? getDefaultPaginationState() let paginatedRows = rows if (pageSize !== Infinity || pageIndex !== 0) { const pageStart = pageSize * pageIndex const pageEnd = pageStart + pageSize paginatedRows = rows.slice(pageStart, pageEnd) }

pageSize:每页行数

pageSize决定每页展示的行数。它有两条重要规则:

  1. 最小值为 1setPageSize内部会执行Math.max(1, ...)钳制,防止出现 0 或负数的非法每页行数(见 rowPaginationFeature.utils.ts)。
  2. Infinity表示全部行放在单页:这是接口注释明确声明的特殊用法。当pageSizeInfinity时,分页切片逻辑整体被跳过(见上面createPaginatedRowModelif分支),等价于“不分页,一次展示所有行”。

从源码看,Infinity不仅在切片阶段被特殊处理,在页数计算中同样生效:table_getPageCount遇到pageSize === Infinity且行数有限时直接返回1(见 rowPaginationFeature.utils.ts):

if (pageSize === Infinity && Number.isFinite(rowCount) && rowCount > 0) { return 1 } return Math.ceil(rowCount / pageSize)

默认状态与初始值

PaginationState的默认值由getDefaultPaginationState()提供:pageIndex: 0pageSize: 10(见 rowPaginationFeature.utils.ts)。这两个默认常量被resetPaginationresetPageIndexresetPageSize等重置 API 在传入defaultState: true时复用。

rowPaginationFeature在初始化时通过getInitialState合并默认值与用户传入的initialState.pagination

getInitialState: (initialState) => { return { ...initialState, pagination: { ...getDefaultPaginationState(), ...initialState.pagination, }, } }

也就是说,你可以通过initialState: { pagination: { pageIndex: 1, pageSize: 20 } }指定表格启动时的页码与每页行数,未指定的字段自动回落到默认值。示例代码中该用法的注释形式为// initialState: { pagination: { pageIndex: 1, pageSize: 20 } }, // set the initial page once(见 examples/react/pagination/src/main.tsx)。

分页状态的管理方式

PaginationState作为TableStatepagination切片的核心类型,支持三种主流管理模式,示例代码中对三者均有注释演示(见 examples/react/pagination/src/main.tsx):

1. 内部自动状态(默认)

不显式传入stateatoms,表格内部自行持有pagination状态。rowPaginationFeature的默认选项会通过makeStateUpdater('pagination', table)生成onPaginationChange(见 rowPaginationFeature.ts),导航 API 调用时自动更新内部状态。

2. 受控状态(classic controlled state)

显式传入state: { pagination },并搭配onPaginationChange: setPagination回调将新状态写回外部。这种模式适合把分页状态与路由参数、全局 store 同步。

3. 外部原子(external atoms,推荐)

通过atoms: { pagination: paginationAtom }让外部原子持有该状态切片。类型定义中明确指出:“external atoms can own the slice without this callback”(见 rowPaginationFeature.types.ts),即使用外部原子时无需onPaginationChange,状态写入直接作用在原子实例上。

围绕 PaginationState 构建的表格 API

PaginationState是分页功能的“数据面”,围绕它Table_RowPagination接口提供了一整套“行为面” API(见 rowPaginationFeature.types.ts),全部由rowPaginationFeatureconstructTableAPIs中注册(见 rowPaginationFeature.ts):

状态读取与页数解析

  • table.getPageCount():解析当前总页数。优先级为options.pageCount(手动分页时显式提供)> 由rowCount / pageSize计算,且对pageSize === Infinity返回 1(见 rowPaginationFeature.utils.ts)。
  • table.getRowCount():解析分页所用总行数。options.rowCount优先,否则取getPrePaginatedRowModel().rows.length,即过滤、分组、排序、展开之后、分页切片之前的行数(见 rowPaginationFeature.utils.ts)。
  • table.getPageOptions():返回零基页码数组,如[0, 1, ..., pageCount - 1],未知或空页数时返回空数组(见 rowPaginationFeature.utils.ts)。

导航能力检测

  • table.getCanPreviousPage()pageIndex > 0时返回true(见 rowPaginationFeature.utils.ts)。
  • table.getCanNextPage()pageCount === -1(未知页数)时返回truepageCount === 0时返回false;否则判断pageIndex < pageCount - 1(见 rowPaginationFeature.utils.ts)。
  • table.getCanLastPage():仅当页数为有限值且pageIndex < pageCount - 1时返回true(见 rowPaginationFeature.utils.ts)。

翻页与跳页

  • table.firstPage()/table.previousPage()/table.nextPage()/table.lastPage():分别跳转到第一页、上一页、下一页、最后一页。previousPagenextPage内部都委托给table_setPageIndex的 updater 形式(old - 1/old + 1),保证状态所有权与 updater 语义一致(见 rowPaginationFeature.utils.ts)。
  • table.setPageIndex(updater):直接更新pageIndex,支持值或函数式 updater,并按已知页数钳制在[0, pageCount - 1]之间;当pageCountundefined-1时不限上限(见 rowPaginationFeature.utils.ts)。
  • table.setPageSize(updater):更新pageSize(最小值钳制为 1),同时保持当前顶部行可见——重算pageIndex = Math.floor(topRowIndex / pageSize);从Infinity切回有限值时顶行索引归零(见 rowPaginationFeature.utils.ts)。这一设计在测试中也有覆盖,例如从pageSize: Infinity切到10时得到{ pageIndex: 0, pageSize: 10 }(见 rowPaginationFeature.utils.test.ts)。
  • table.setPagination(updater):以整个PaginationState为粒度的更新入口,接受完整对象或函数式 updater(见 rowPaginationFeature.utils.ts)。

重置

  • table.resetPagination(defaultState?):无参时恢复initialState.pagination,否则恢复{ pageIndex: 0, pageSize: 10 }(见 rowPaginationFeature.utils.ts)。
  • table.resetPageIndex(defaultState?)/table.resetPageSize(defaultState?):分别只重置单个字段,同样支持“优先initialState、传true用默认值”的两段式语义(见 rowPaginationFeature.utils.ts)。

手动分页(服务端分页)模式下的字段配合

PaginationState不关心数据来自客户端还是服务端,它只是纯粹的状态载体。当启用服务端分页时,通过manualPagination: true关闭客户端自动切片,表格期待传入的数据本身已经是分页后的结果,此时需要配合以下选项:

  • manualPagination: boolean:启用手动分页,getPaginatedRowModel不再对行做切片(类型注释见 rowPaginationFeature.types.ts)。
  • pageCount: number:已知总页数时显式传入;不确定时设为-1,此时getCanNextPage恒返回truegetPageOptions返回空数组、setPageIndex不钳制上限(见 rowPaginationFeature.types.ts 与对应工具函数实现)。
  • rowCount: number:已知总行数时传入,pageCount会由rowCount / pageSize自动计算(见 rowPaginationFeature.types.ts)。table_getRowCountoptions.rowCount优先取值,验证了这一优先级(见 rowPaginationFeature.utils.ts)。

相关测试证实:当manualPagination: truepageCount: 7时,页数解析直接采用配置值而非按行数计算(见 rowPaginationFeature.utils.test.ts)。

页码自动重置(autoResetPageIndex)

PaginationState还关联一个重要行为:当数据更新、过滤、排序、分组等“影响页码归属”的状态变化发生时,autoResetPageIndex(默认true,且受autoResetAll全局开关与!manualPagination兜底共同控制)会将页码重置回首页,避免用户停留在已失效的页码上。其实现逻辑为:若当前pageIndex已是 0 则直接跳过,避免无意义的回调副作用(例如服务端分页场景下每次过滤都触发一次多余的onPaginationChange重取数据);否则执行resetPageIndex(table, true)(见 rowPaginationFeature.utils.ts)。

关闭自动重置的写法(示例代码注释中给出):

// autoResetPageIndex: false, // keep the current page after page-altering changes; default true // autoResetAll: false, // turn off every feature's automatic reset, including page index

客户端分页的完整接入流程

开启客户端分页需要在功能装配阶段同时注册rowPaginationFeaturecreatePaginatedRowModel()(见 examples/react/pagination/src/main.tsx):

const features = tableFeatures({ rowPaginationFeature, paginatedRowModel: createPaginatedRowModel(), })

其中createPaginatedRowModel返回的是基于tableMemo记忆化行模型工厂,其依赖为“分页前行模型 +atoms.pagination值 + 未启用paginateExpandedRows时的展开状态”(见 createPaginatedRowModel.ts),因此只有分页相关状态真正变化时才会重建切片结果。

随后即可在渲染层消费状态与 API:

// 导航 <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.getCanLastPage()}>{'>>'}</button> // 页码显示(0 基转 1 基) <span>Page {(table.state.pagination.pageIndex + 1).toLocaleString()} of {table.getPageCount().toLocaleString()}</span> // 跳页 <input type="number" min="1" max={table.getPageCount()} value={table.state.pagination.pageIndex + 1} onChange={(e) => table.setPageIndex(Number(e.target.value) - 1)} /> // 每页行数(含“显示全部”) <select value={table.state.pagination.pageSize} onChange={(e) => table.setPageSize(Number(e.target.value))}> {[10, 20, 30, 40, 50].map((pageSize) => ( <option key={pageSize} value={pageSize}>Show {pageSize}</option> ))} <option value={Infinity}>Show All</option> </select> // 行数统计 <span>Showing {table.getRowModel().rows.length.toLocaleString()} of {table.getRowCount().toLocaleString()} Rows</span>

完整的可运行示例见 examples/react/pagination,该示例默认生成 1000 行数据,并提供“Stress Test (1M rows)”按钮验证pageSize: Infinity单页展示百万行数据时的行为。

小结

PaginationState虽只有pageIndexpageSize两个字段,却是 TanStack Table 分页体系的“总开关”:

  • pageIndex采用 0 基语义,驱动slice(pageSize * pageIndex, pageStart + pageSize)的切片计算;
  • pageSize最小为 1,Infinity表示单页全量展示,并影响页数解析与setPageSize的顶行保持策略;
  • 状态可通过内部自持、受控state+onPaginationChange、外部原子atoms三种方式管理;
  • 配合manualPaginationpageCountrowCountautoResetPageIndex等选项,同一套状态模型即可覆盖客户端分页与服务端分页两种场景。

若需进一步深入,可继续阅读 features.md 中关于分页特性在整体插件架构中的定位,或直接研读分页功能的核心源码 rowPaginationFeature.ts 及其配套工具与测试文件。

【免费下载链接】table🤖 Headless UI for building powerful tables & datagrids for TS/JS - React-Table, Vue-Table, Solid-Table, Svelte-Table项目地址: https://gitcode.com/gh_mirrors/ta/table

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询