news 2026/9/12 9:26:43

Refine v5 useTable 实战指南:用 Ant Design Table 快速构建支持分页、排序与筛选的后台列表页

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Refine v5 useTable 实战指南:用 Ant Design Table 快速构建支持分页、排序与筛选的后台列表页

Refine v5 useTable 实战指南:用 Ant Design Table 快速构建支持分页、排序与筛选的后台列表页

【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards & B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine

useTable是 Refine v5 中面向 Ant Design 的表格 Hook,它返回与 Ant Design<Table>示例为实战主线,结合 packages/antd/src/hooks/table/useTable/useTable.ts 源码与 完整 Hook 文档,讲清楚它的用法、配置项、返回值以及常见问题的解法,读完即可在真实 React 管理后台中落地一套完整的列表页。

为什么选择 useTable

在 Refine v5 中,useTable让你"不需要为排序、筛选和分页做任何额外操作",就能拿到与 Ant Design<Table>组件兼容的全部属性。它的核心定位可以从源码中的注释得到印证:

By using useTable, you are able to get properties that are compatible with Ant Design<Table>component. All features such as sorting, filtering and pagination comes as out of box.

从 useTable.ts 的实现来看,它本身是@refinedev/coreuseTable的扩展:内部先调用核心层的useTableCore完成数据请求与状态管理,再通过mapAntdSorterToCrudSortingmapAntdFilterToCrudFilter把 Ant Design 的表格交互翻译成 Refine 的CrudSorting/CrudFilters。而数据获取这一层,底层走的是useListHook,也就是说表格数据本质上是一次getList数据提供者调用。

useTable完整可用性由单元测试覆盖,见 useTable.spec.tsx:测试用例覆盖了默认渲染、自定义分页初始值、自定义资源、syncWithLocationsetFilters手动设置筛选、defaultBehavior: "replace"、分页模式client/server/off、从 URL 参数回填搜索表单等场景,可作为理解其行为边界的权威参考。

快速上手:最小可用列表页

以示例项目 examples/table-antd-use-table 为例,其数据源为https://api.fake-rest.refine.dev,资源为posts(见 App.tsx)。

一个最小可用的useTable列表页大致如下:

import { List, useTable } from "@refinedev/antd"; import { Table } from "antd"; export const PostList = () => { const { tableProps } = useTable<IPost>(); return ( <List> <Table {...tableProps} rowKey="id"> <Table.Column dataIndex="id" title="ID" /> <Table.Column dataIndex="title" title="Title" /> </Table> </List> ); };

这里有几个要点:

  • tableProps展开后自动携带dataSourceloadingonChangepaginationscroll: { x: true }<Table>需要的属性,无需手动接线;
  • 默认情况下,useTable会从当前路由推断resource(示例中/posts路由对应posts资源),无需显式声明;
  • rowKey="id"是 Ant Design<Table>渲染所必需的,通常由使用方提供。

分页:服务端分页与三种模式

分页能力由tableProps.pagination开箱即用地提供。从 useTable.ts 源码可以看到,antdPagination()会生成完整的TablePaginationConfig:包含current(当前页)、pageSizetotal(来自data?.total)、以及响应式位置(小屏居底居中、大屏居右下)。更关键的是,它会通过createLinkForSyncWithLocation为页码生成真实链接而非纯状态切换,并覆盖<Table>默认的pagination.itemRender

覆盖分页配置

如果你想调整<Table>的分页展示,正确的姿势是把tableProps.pagination展开后再覆盖:

const { tableProps } = useTable<IPost>(); <Table {...tableProps} rowKey="id" pagination={{ ...tableProps.pagination, position: ["bottomCenter"], size: "small", }} > {/* columns */} </Table>

分页模式(pagination.mode)

pagination.mode可选"server"(默认)、"client""off"

  • server:默认模式,服务端分页,请求携带currentPagepageSize
  • client:客户端分页,一次性拉取全部记录后在浏览器端分页;
  • off:禁用分页,拉取所有记录,此时tableProps.paginationfalse(源码中isPaginationEnabledfalseantdPagination()直接返回false)。
useTable({ pagination: { mode: "client", }, });

对应测试可见 useTable.spec.tsx:client/server模式返回{ pageSize: 10, current: 1 },而off模式下tableProps.pagination为假值。

初始分页值

useTable({ pagination: { currentPage: 2, // 默认 1 pageSize: 20, // 默认 10 }, });

排序:列级 sorter 与服务端排序

要给列开启排序,只需给对应的<Table.Column>加上sorter属性,useTable会自动把 Ant Design 的排序状态映射为CrudSorting并随请求发送给服务端:

<Table.Column dataIndex="id" title="ID" sorter />

有几个值得注意的细节:

  • 请求中使用的字段名优先取<Column>key,没有key时回退到dataIndex,因此当dataIndex与排序字段名不一致时可以用key指定;
  • 多列排序时,sorter需要传入{ multiple: 1 }{ multiple: 2 }等值来声明优先级。示例 list.tsx 中content列为multiple: 1title列为multiple: 2
  • 排序状态同样支持通过syncWithLocation同步到 URL。

初始排序与永久排序

sorters.initial用于设置初始排序,它会被用户的后续操作清除;如果需要不可修改的固定排序,使用sorters.permanent

useTable({ sorters: { initial: [{ field: "name", order: "asc" }], // permanent: [{ field: "name", order: "asc" }], }, });

sorters.mode可设为"off""server"(默认)。设为"off"时排序值不发送给服务端,可与 Ant Design<Table>的客户端排序(如sorter={(a, b) => a.id - b.id})配合使用,适合数据量小的场景。

筛选:FilterDropdown 与列级筛选

列级筛选依赖<Table.Column>filterDropdown属性,把筛选表单放入 Refine 提供的<FilterDropdown>组件中,并将函数入参透传给该组件:

<Table.Column dataIndex="status" title="Status" render={(value: string) => <TagField value={value} />} filterDropdown={(props) => ( <FilterDropdown {...props}> <Radio.Group> <Radio value="published">Published</Radio> <Radio value="draft">Draft</Radio> <Radio value="rejected">Rejected</Radio> </Radio.Group> </FilterDropdown> )} />

在 list.tsx 中可以同时看到字符串筛选(Radio.Group)、文本包含筛选(Input)和关联数据多选筛选(Select mode="multiple")三种形态;其中关联筛选通过mapValue把选项值转换为数字后再交给FilterDropdown

初始筛选、默认值与 helper 函数

设置了filters.initial时,必须配合getDefaultSortOrder/defaultFilteredValue让表格列状态与 Hook 状态同步:

const { tableProps, sorters, filters } = useTable({ sorters: { initial: [{ field: "title", order: "asc" }], }, filters: { initial: [{ field: "status", operator: "eq", value: "published" }], }, }); <Table.Column dataIndex="title" title="Title" defaultSortOrder={getDefaultSortOrder("title", sorters)} />; <Table.Column dataIndex="status" title="Status" defaultFilteredValue={getDefaultFilter("status", filters)} filterDropdown={(props) => ( <FilterDropdown {...props}>{/* ... */}</FilterDropdown> )} />;
  • getDefaultSortOrder(field, sorters)getDefaultFilter(field, filters, operator?)@refinedev/antd导出的工具函数,用于从当前sorters/filters状态中取出指定字段的默认值;
  • 示例 list.tsx 一次性配置了三个初始筛选(title包含空串、status等于draftcategory.id属于[1, 2])和一个初始排序(title升序),并全部回填到列上,是完整的参考实现。

筛选行为与永久筛选

filters.initial会被后续操作清除,filters.permanent则恒定生效。filters.defaultBehavior控制setFilters时的合并策略,默认"merge"(相同字段替换、不同字段追加),可改为"replace"(整体替换):

useTable({ filters: { defaultBehavior: "replace", }, });

对应测试 useTable.spec.tsx 验证了"replace"模式下新筛选会完全覆盖旧筛选。filters.modesorters.mode类似,可设为"off"关闭服务端筛选,配合<Table.Column>filters+onFilter做纯客户端筛选。

搜索表单:onSearch 与 searchFormProps

useTable还额外提供了onSearch配置与searchFormProps返回值,用来构造独立的搜索表单。onSearch接收表单提交值并返回CrudFiltersPromise<CrudFilters>,提交后自动把页码重置为 1(见 useTable.ts 中onFinish的实现):

import { List, useTable, SaveButton } from "@refinedev/antd"; import { Table, Form, Input } from "antd"; interface IPost { id: number; title: string; } interface ISearch { title: string; } const PostList = () => { const { searchFormProps, tableProps } = useTable<IPost, HttpError, ISearch>({ onSearch: (values) => [ { field: "title", operator: "contains", value: values.title, }, ], }); return ( <List> <Form {...searchFormProps} layout="inline"> <Form.Item name="title"> <Input placeholder="Search by title" /> </Form.Item> <SaveButton onClick={searchFormProps.form?.submit} /> </Form> <Table {...tableProps} rowKey="id"> <Table.Column dataIndex="id" title="ID" /> <Table.Column title="Title" dataIndex="title" /> </Table> </List> ); };

searchFormProps展开后即 Ant Design<Form>的 props,其onFinish已被useTable接管;配合syncWithLocation时,URL 参数中的筛选值会自动回填到表单字段(useTable.ts 中通过注册表单字段与filters的匹配完成回填,测试见 useTable.spec.tsx)。

URL 状态同步:syncWithLocation

syncWithLocation是后台管理场景的高频需求:开启后,分页、排序、筛选状态会编码进 URL 查询参数,URL 变化时表格状态自动跟随,从而支持分享、收藏与刷新后状态保持。可以在 Hook 级开启:

useTable({ syncWithLocation: true, });

也可以在<Refine>组件上全局开启(示例 App.tsx 即使用全局配置options.syncWithLocation: true)。此时antdPagination()生成的页码链接会携带完整的paginationsortersfilters状态,实现真正的"可分享的表格视图"。

数据请求与运行时配置

由于底层使用useList(即 React Query 的useQuery),useTable透传了一系列数据请求与实时能力:

useTable({ resource: "categories", // 显式指定资源,默认从路由推断 dataProviderName: "second-data-provider", // 多数据提供者时选择目标 queryOptions: { retry: 3 }, // 透传给 useQuery meta: { headers: { "x-meta-data": "true" } }, // 传给数据提供者方法的附加信息 successNotification: (data, values, resource) => ({ message: `${data.title} Successfully fetched.`, description: "Success with no errors", type: "success", }), errorNotification: (data, values, resource) => ({ message: `Something went wrong when getting ${data.id}`, description: "Error", type: "error", }), });
  • resource:默认从路由推断;多资源同名时可用identifier指定主匹配键;
  • meta:可向getList等数据提供者方法传递额外信息(如自定义请求头、GraphQL 查询字段),数据提供者从方法参数中读取meta
  • successNotification/errorNotification:依赖NotificationProvider,自定义请求成功 / 失败的通知内容;
  • liveModeonLiveEventliveParams:依赖LiveProvider,用于实时订阅数据更新(liveMode: "auto"自动刷新、"manual"手动处理);
  • overtimeOptions:设置请求超时提示,interval为轮询间隔(毫秒),onInterval为回调,返回的overtime.elapsedTime在请求完成后变为undefined,可用于"请求超过 4 秒显示提示"之类的交互。

返回值速查

useTable的返回值中,除tablePropssearchFormProps外,还暴露了可编程控制的状态:

返回值说明
tableProps直接传给<Table>的 props(含dataSourceloadingonChangepaginationscroll)。注意onChange内部负责排序 / 筛选 / 分页处理,覆盖后需自行实现这些逻辑
searchFormPropsAnt Design<Form>props,onFinish触发onSearch
tableQuery底层useList(React QueryuseQuery)的返回值,如isSuccessisFetching
sorters/setSorters当前排序状态与更新函数
filters/setFilters当前筛选状态与更新函数(支持merge/replace行为或函数式更新)
currentPage/setCurrentPage当前页码及更新函数(分页关闭时为undefined
pageSize/setPageSize每页条数及更新函数(分页关闭时为undefined
pageCount总页数(分页关闭时为undefined
createLinkForSyncWithLocation根据pagination/sorters/filters生成可分享链接的函数
overtime{ elapsedTime?: number },超时加载状态

这些返回值在 useTable.ts 中有完整定义,类型签名(如setFilters支持behavior?: "merge" | "replace")可参考 Hook 文档 的 API 一节。

常见问题

如何处理关联数据?当列表列需要展示关联资源名称(如category.id→ 分类标题)时,用useMany批量拉取关联数据,并用useSelect配合getDefaultFilter构建分类多选筛选。示例 list.tsx 展示了完整做法:从tableProps.dataSource收集category.id列表 →useMany取分类 → 渲染时查表映射标题。

如何做纯客户端筛选 / 排序?分别设置filters.mode: "off"sorters.mode: "off",即可关闭服务端筛选 / 排序,完全复用 Ant Design<Table>自带的filters+onFiltersorter客户端能力,适合数据量不大、不希望每次交互都发请求的场景。

小结

useTable把"表格数据获取 + 分页 + 排序 + 筛选 + 搜索 + URL 状态同步"完整收敛到一个 Hook 中:外层是 Ant Design<Table>的零成本接入,内层是 Refine 核心的useList数据请求与CrudSorting/CrudFilters统一模型。无论是用 table-antd-use-table 示例 快速起步,还是深入 useTable 源码 理解其映射逻辑,都能在真实后台项目中显著减少列表页的样板代码。

【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards & B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/12 9:22:53

不用下载到本地:kkFileView 文件在线预览服务搭建指南

不用下载到本地&#xff1a;kkFileView 文件在线预览服务搭建指南 【免费下载链接】kkFileView Universal File Online Preview Project based on Spring-Boot 项目地址: https://gitcode.com/GitHub_Trending/kk/kkFileView 收到文件想先扫一眼内容&#xff0c;你是不是…

作者头像 李华
网站建设 2026/9/12 9:18:40

mailcow 邮件服务器 Docker 部署:30 分钟从零到可用

mailcow 邮件服务器 Docker 部署&#xff1a;30 分钟从零到可用 【免费下载链接】mailcow-dockerized mailcow: dockerized - &#x1f42e; &#x1f40b; &#x1f495; 项目地址: https://gitcode.com/GitHub_Trending/ma/mailcow-dockerized mailcow-dockerized 是…

作者头像 李华
网站建设 2026/9/12 9:17:06

钉钉与微信小程序开发对比及跨平台实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/12 9:15:01

Jetpack Compose实现Material Design双行卡片组件开发

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华