拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Refine + TanStack Table 分页实战:基于 @refinedev/react-table 的 useTable 分页实现与原理

Refine TanStack Table 分页实战基于 refinedev/react-table 的 useTable 分页实现与原理【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine本指南以 Refine 官方文档中useTable分页章节的可运行示例_partial-pagination-live-preview.md为骨架完整讲解如何用refinedev/react-table在 Refine v5 项目中实现服务端/客户端分页表格从安装、完整可复制的分页 UI 代码到setPageIndex、setPageSize、getPrePaginationRowModel等 TanStack Table API 的逐一拆解并结合refinedev/react-table的源码packages/react-table/src/useTable/index.ts与测试用例packages/react-table/src/useTable/index.spec.ts讲清底层工作原理。读完你可以独立搭建一个带页码跳转、上一页/下一页、每页条数切换的 headless 分页表格。背景headless 的 TanStack Table 适配器Refine 官方为 TanStack Table 中明确说明所有 TanStack Table 的特性都被支持官方示例可以直接复制粘贴到项目中使用。useTable是从refinedev/core包中的useTable扩展而来底层通过useList获取数据。也就是说你既能使用 coreuseTable的全部能力资源解析、数据提供器对接、syncWithLocation、实时更新等也能使用 TanStack Table 的全部表格能力。安装使用你习惯的包管理器安装适配包npm install refinedev/react-table # 或 pnpm add refinedev/react-table # 或 yarn add refinedev/react-table该包内部依赖tanstack/react-tableRefine 会将其作为依赖一并安装无需手动安装 TanStack Table 本身。完整示例一个带分页控制的帖子列表下面这段代码完整来自官方分页示例见 documentation/docs/packages/tanstack-table/examples/_partial-pagination-live-preview.md。它渲染一个posts资源列表并在表格下方构建了一个完全自绘的分页条首页/上一页/下一页/末页四个按钮、当前页码与总页数、跳页输入框、每页条数下拉选择以及总行数统计。import React from react; import { useTable } from refinedev/react-table; import { ColumnDef, flexRender } from tanstack/react-table; interface IPost { id: number; title: string; content: string; status: published | draft | rejected; } const PostList: React.FC () { const columns React.useMemoColumnDefIPost[]( () [ { id: id, header: ID, accessorKey: id, }, { id: title, header: Title, accessorKey: title, }, { id: status, header: Status, accessorKey: status, }, { id: createdAt, header: CreatedAt, accessorKey: createdAt, }, ], [], ); const { reactTable: { getHeaderGroups, getRowModel, // 分页相关 API getState, setPageIndex, getCanPreviousPage, getPageCount, getCanNextPage, nextPage, previousPage, setPageSize, getPrePaginationRowModel, }, } useTable({ columns, }); return ( div table thead {getHeaderGroups().map((headerGroup) ( tr key{headerGroup.id} {headerGroup.headers.map((header) { return ( th key{header.id} {header.isPlaceholder ? null : ( {flexRender( header.column.columnDef.header, header.getContext(), )} / )} /th ); })} /tr ))} /thead tbody {getRowModel().rows.map((row) { return ( tr key{row.id} {row.getVisibleCells().map((cell) { return ( td key{cell.id} {flexRender( cell.column.columnDef.cell, cell.getContext(), )} /td ); })} /tr ); })} /tbody /table {/* 分页 UI 可以任意自绘这里只是最基础的实现 */} div button onClick{() setPageIndex(0)} disabled{!getCanPreviousPage()} {} /button button onClick{() previousPage()} disabled{!getCanPreviousPage()} {} /button button onClick{() nextPage()} disabled{!getCanNextPage()} {} /button button onClick{() setPageIndex(getPageCount() - 1)} disabled{!getCanNextPage()} {} /button span divPage/div strong {getState().pagination.pageIndex 1} of {getPageCount()} /strong /span span | Go to page: input typenumber defaultValue{getState().pagination.pageIndex 1} onChange{(e) { const page e.target.value ? Number(e.target.value) - 1 : 0; setPageIndex(page); }} / /span select value{getState().pagination.pageSize} onChange{(e) { setPageSize(Number(e.target.value)); }} {[10, 20, 30, 40, 50].map((pageSize) ( option key{pageSize} value{pageSize} Show {pageSize} /option ))} /select /div div{getPrePaginationRowModel().rows.length} Rows/div /div ); };把PostList挂到路由上并确保Refine中注册了posts资源与对应数据提供器即可运行。分页 API 逐一拆解示例中从useTable()返回的reactTable对象里解构出来的 9 个分页相关 API都是 TanStack Table v8 的标准接口API作用示例中的用法getState()读取当前表格状态含pagination.pageIndex、pagination.pageSizegetState().pagination.pageIndex 1显示当前页TanStack 的pageIndex从 0 开始显示时 1setPageIndex(index)跳转到指定页0 基索引首页按钮setPageIndex(0)末页按钮setPageIndex(getPageCount() - 1)跳页输入框把用户输入的 1 基页码减 1 后传入getCanPreviousPage()是否存在上一页即是否在第一页作为、按钮的disabled条件getPageCount()总页数末页按钮与Page x of y展示getCanNextPage()是否存在下一页即是否在末页作为、按钮的disabled条件nextPage()下一页按钮点击事件previousPage()上一页按钮点击事件setPageSize(size)设置每页条数select的onChange取值来自[10, 20, 30, 40, 50]getPrePaginationRowModel()返回分页前的行模型全量行getPrePaginationRowModel().rows.length展示总行数两点关键提示pageIndex是 0 基、currentPage是 1 基TanStack Table 内部pageIndex从 0 开始计数因此显示页码要1跳页输入框要把用户输入减 1。跳页输入的边界处理const page e.target.value ? Number(e.target.value) - 1 : 0;—— 输入为空时回退到第 0 页第一页避免NaN传入。底层原理useTable 是如何把分页与 Refine 数据请求联动起来的官方分页章节文档指出「setPageSize等方法的每次变更都会触发一次新的数据提供器请求」。要理解这一点需要看 packages/react-table/src/useTable/index.ts 的实现const isPaginationEnabled refineCoreProps.pagination?.mode ! off; // ... const reactTableResult useReactTableTData({ data: data?.data ?? [], // ... initialState: { pagination: { pageIndex: currentPage - 1, pageSize: pageSizeCore, }, // ... }, pageCount, manualPagination: true, // ... });关键机制有三条manualPagination: true手动分页TanStack Table 不再在客户端自动切片data而是把分页状态完全交给开发者/外层框架控制配合 Refine 的服务端数据获取。初始状态双向映射coreuseTable的currentPage1 基映射为 TanStack 的pageIndex0 基即currentPage - 1pageSize直接透传。同时把 core 返回的pageCount作为 TanStack 的总页数来源。状态同步useEffect 反向联动当用户在 UI 上调用setPageIndex/setPageSize改变 TanStack 状态后两个useEffect会把新的pageIndex 1与pageSize写回 core 的setCurrentPage/setPageSizeCore从而触发useList重新请求数据useEffect(() { if (pageIndex ! undefined) { setCurrentPage(pageIndex 1); } }, [pageIndex]); useEffect(() { if (pageSize ! undefined) { setPageSizeCore(pageSize); } }, [pageSize]);测试用例印证了这一行为packages/react-table/src/useTable/index.spec.ts默认无参数时state.pagination?.pageIndex为0、pageSize为10、pageCount为1传入pagination: { currentPage: 2, pageSize: 1 }后core 侧currentPage为2、pageSize为1TanStack 侧pageIndex为1、pageSize为1pageCount变为3。配置项pagination.currentPage / pageSize / mode分页行为通过useTable的refineCoreProps.pagination配置完整参数表见 useTable 官方文档useTable({ refineCoreProps: { pagination: { currentPage: 2, // 初始页码1 基默认 1 pageSize: 10, // 初始每页条数默认 10 mode: server, // server | client | off默认 server }, }, });参数类型默认值说明pagination.currentPagenumber1初始页码1 基pagination.pageSizenumber10初始每页条数pagination.modeoff \| server \| clientserver分页模式见下三种mode的含义server默认服务端分页。数据提供器根据currentPage与pageSize请求对应页的数据TanStack Table 通过manualPagination: true不做客户端切片。这也是示例中getPrePaginationRowModel().rows.length能拿到总数的原因——总行数来自服务端返回的total。client客户端分页。一次性拉取全量记录由 TanStack Table 在客户端完成切片。适用于数据量较小的场景。off关闭分页一次请求拉取全部记录。从源码看mode: off时isPaginationEnabled为false同时排序sorting.length 0或筛选crudFilters.length 0变化后只有分页启用时才会把页码重置回第 1 页if (sorting.length 0 isPaginationEnabled !isFirstRender) { setCurrentPage(1); }与排序、筛选的联动换页重置除了分页本身源码中还有两处值得注意的联动逻辑packages/react-table/src/useTable/index.ts 第 135-176 行排序变化sorting变更时将 TanStack 的排序状态转换回 Refine 的CrudSortingdesc↔desc其余为asc并通过setSorters写回 core触发重新请求列筛选变化columnFilters变更时通过工具函数columnFiltersToCrudFilters/crudFiltersToColumnFilters与 core 的 filters 状态双向同步并补回被移除的筛选器getRemovedFilters。二者的共同点是在分页启用且非首次渲染时把页码重置回第 1 页避免用户停留在第 5 页时排序/筛选导致越界。测试用例同样覆盖了这些行为index.spec.ts中针对sorters.mode、filters.mode的it.each用例验证manualSorting/manualFiltering与服务端模式的对应关系。URL 状态同步syncWithLocation如果希望分页状态以及其他表格状态同步到 URL 查询参数从而支持收藏、分享链接可以启用syncWithLocationuseTable({ refineCoreProps: { syncWithLocation: true, }, });启用后pageIndex、pageSize等状态会自动编码进 URL queryURL 变化时表格状态也会随之恢复。默认它会读取Refine组件上的syncWithLocation全局配置。进阶客户端分页与关闭分页按数据规模选择模式// 客户端分页全量拉取、本地切片 useTable({ refineCoreProps: { pagination: { mode: client, }, }, }); // 关闭分页一次拉取全部记录 useTable({ refineCoreProps: { pagination: { mode: off, }, }, });注意客户端分页模式下TanStack Table 的getFilteredRowModel、getSortedRowModel等客户端行模型仍然可用源码中仅在对应服务端模式开启时才置为undefined因此客户端分页可以方便地与客户端排序/筛选组合使用。参考示例仓库内完整的可运行示例见 examples/table-react-table-basic它与本指南的分页示例同源对应文档页面的 CodeSandbox 链接。相关文档还包括useTable Hook 完整参考属性 / 返回值 / FAQTanStack Table 集成介绍Tables 指南guides-conceptscore useTable 数据 Hook 文档适配器源码 与 测试用例【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门