Refine v5 + Chakra UI 高级表格实战:基于 @refinedev/react-table 实现筛选、排序、批量删除与行内编辑 📅 发布时间:2026/9/12 4:53:22 👁 浏览次数: Refine v5 Chakra UI 高级表格实战基于 refinedev/react-table 实现筛选、排序、批量删除与行内编辑【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine本指南以 Refine 仓库中的 Chakra UI Advanced React Table 示例关联文档为核心完整讲解如何基于refinedev/react-table适配器在 Chakra UI 项目中构建可投入真实管理后台的高阶表格。读完你将掌握表格列的声明式定义、列筛选与列排序、行多选与批量删除、行展开、行内编辑以及分页组件的完整实现方案。示例定位在 Basic 表格之上叠加生产级能力该示例对应的项目位于 examples/table-chakra-ui-advanced其文档标题为 Advanced React Table Example | Pagination in Refine v5明确说明这是 Chakra UI 体系下的高级表格示例。与基础版 table-chakra-ui-basic 相比它在原有列表展示 单行操作的基础上将删除、编辑、筛选三大能力整合进同一张表格支持列级筛选、列级排序、表头多选与批量删除、点击行内编辑按钮就地渲染编辑表单、展开行查看详情等真实后台高频交互。从技术选型看该示例并非自行封装表格逻辑而是基于 Refine 官方表格适配器refinedev/react-tablev6.0.1Refine 对 TanStack React Tabletanstack/react-tablev8.2.6的适配层负责把 Refine 的数据获取、分页、排序、筛选状态与 TanStack 表格状态同步refinedev/corev5.0.12提供useTable底层的数据请求、useDeleteMany、useMany、useSelect等核心 hooksrefinedev/react-hook-formv5.0.4为行内编辑提供表单能力refinedev/chakra-uiv3.0.2提供List、EditButton、DeleteButton、SaveButton、DateField等 UI 组件与usePagination。依赖清单可在 examples/table-chakra-ui-advanced/package.json 中确认其脚本为标准的refine dev/refine build/refine startNode 环境要求20。应用骨架Refine Chakra UI 的最小装配在进入表格实现之前先看示例的入口装配 src/App.tsx它演示了 Refine 应用的标准初始化方式import { GitHubBanner, Refine } from refinedev/core; import { ErrorComponent, ThemedLayout, RefineThemes, useNotificationProvider, } from refinedev/chakra-ui; import { ChakraProvider } from chakra-ui/react; import dataProvider from refinedev/simple-rest; import routerProvider, { NavigateToResource, UnsavedChangesNotifier, DocumentTitleHandler, } from refinedev/react-router; import { BrowserRouter, Routes, Route, Outlet } from react-router;核心配置如下ChakraProvider theme{RefineThemes.Blue} Refine notificationProvider{useNotificationProvider()} routerProvider{routerProvider} dataProvider{dataProvider(https://api.fake-rest.refine.dev)} resources{[ { name: posts, list: /posts }, ]} options{{ syncWithLocation: true, warnWhenUnsavedChanges: true, }} Routes Route element{ ThemedLayout Outlet / /ThemedLayout } Route index element{NavigateToResource resourceposts /} / Route path/posts element{PostList /} / Route path* element{ErrorComponent /} / /Route /Routes UnsavedChangesNotifier / DocumentTitleHandler / /Refine /ChakraProvider几个值得注意的要点数据提供器示例使用refinedev/simple-rest连接公开的https://api.fake-rest.refine.devREST API这意味着表格的排序、筛选、分页请求都由 Refine 自动转换成带查询参数的 HTTP 请求发出主题通过ChakraProvider theme{RefineThemes.Blue}注入 Refine 为 Chakra UI 预设的主题路由与布局ThemedLayout提供统一的侧边栏与头部布局/posts路由挂载PostList表格页面体验细节syncWithLocation: true让筛选/排序/分页状态同步到 URL便于分享与刷新还原warnWhenUnsavedChanges: true在行内编辑未保存时离开页面会给出提示配合UnsavedChangesNotifier生效。核心 hooks 组合useTable 与 useForm 的分工高级表格的高级之处首先体现在它同时使用了两套表单/表格体系Hook来源职责useTablerefinedev/react-table表格数据、列定义、排序、筛选、分页、行选择、行展开状态useFormrefinedev/react-hook-form行内编辑表单的字段注册、校验与提交页面主体 src/pages/posts/list.tsx 中表单与表格各自解构出自己的状态const { refineCore: { onFinish, id, setId }, saveButtonProps, handleSubmit, register, } useFormIPost({ refineCoreProps: { redirect: false, action: edit, }, });action: edit表示表单以编辑模式工作redirect: false表示提交后不跳转而是停留在表格页并关闭编辑行——这正是行内编辑的典型诉求。id与setId控制当前正在编辑哪一行当setId被设置为某行 ID 时该行渲染为编辑态见下文行内编辑小节。声明式列定义从数据列到交互列的完整映射表格的骨架是React.useMemo包裹的ColumnDefIPost[]列数组。类型定义见 src/interfaces/index.d.tsexport interface IPost { id: number; title: string; content: string; status: published | draft | rejected; category: { id: number }; }列定义覆盖了七类典型场景1. 选择列selection禁用排序与筛选表头渲染全选Checkbox并利用table.getIsAllRowsSelected()/table.getIsSomeRowsSelected()驱动全选与部分选中的视觉状态行单元格渲染单行多选框与展开按钮row.toggleExpanded()这是后续批量删除与行展开的入口。2. 普通数据列id、createdAtcreatedAt列通过 Refine 的DateField组件格式化显示formatLLL并关闭该列筛选。3. 文本筛选列title通过meta.filterOperator: contains声明该列使用包含筛选算子{ id: title, header: Title, accessorKey: title, meta: { filterOperator: contains }, },4. 自定义筛选元素列status不满足于默认的文本输入框示例通过meta.filterElement返回一个 ChakraSelect下拉框配合filterOperator: eq实现精确匹配筛选{ id: status, header: Status, accessorKey: status, meta: { filterElement: function render(props: FilterElementProps) { return ( Select borderRadiusmd sizesm placeholderAll Status {...props} option valuepublishedpublished/option option valuedraftdraft/option option valuerejectedrejected/option /Select ); }, filterOperator: eq, }, },5. 关联数据解析列category.id该列存储的是外键category.id单元格渲染时从table.options.meta.categoriesData中查找对应分类标题找不到时显示 Loading...实现外键 → 可读文本的转换数据来源见后文useMany小节。6. 操作列actions每行渲染EditButton点击触发setId(getValue() as number)进入行内编辑与DeleteButton传入recordItemId执行单行删除并关闭筛选与排序。将列声明交给useTable时示例还设置了初始排序const { ... } useTable({ columns, refineCoreProps: { sorters: { initial: [{ field: id, order: desc }], }, }, });即首次加载按id降序排列。列筛选与列排序两个可复用组件示例将排序与筛选按钮抽成了通用组件位于 src/components/table在表头中与列标题并排渲染HStack spacing2 ColumnSorter column{header.column} / ColumnFilter column{header.column} / /HStackColumnSortercolumnSorter.tsx通过column.getCanSort()判断该列是否允许排序点击触发column.getToggleSortingHandler()图标依据排序状态切换——升序显示IconChevronDown、降序显示IconChevronUp、未排序显示IconSelector。ColumnFiltercolumnFilter.tsx核心逻辑是column.getCanFilter()为假如enableColumnFilter: false的列时直接返回null通过 ChakraMenu弹出筛选面板若列声明了meta.filterElement则渲染自定义筛选组件如 status 列的Select否则回退到默认Input文本框面板内提供清除column.setFilterValue(undefined)与保存column.setFilterValue(state.value)两个按钮确认后才真正写入筛选值并触发 Refine 重新请求数据。这两个组件在基础版示例 table-chakra-ui-basic 中同样被复用说明它们是与业务无关的通用表格能力可以在不同资源间自由迁移。多选与批量删除useDeleteMany 的接入方式批量删除由表头全选勾选框与useDeleteMany协同完成。页面中先获取删除函数const { mutate } useDeleteManyIPost(); const deleteSelectedItems (ids: number[]) { mutate( { resource: posts, ids }, { onSuccess: () { resetRowSelection(); }, }, ); };表头的 Delete 按钮仅在存在选中行时渲染table.getIsSomeRowsSelected() || table.getIsAllRowsSelected()点击后收集所有选中行 ID 并调用deleteSelectedItemsdeleteSelectedItems( table.getSelectedRowModel().flatRows.map(({ original }) original.id), );删除成功onSuccess后调用resetRowSelection()清空行选择状态避免残留幽灵勾选。useDeleteMany来自refinedev/core底层会向数据提供器发送批量删除请求REST 场景下通常是DELETE /posts/{id}逐条或批量端点并自动触发列表数据刷新。行展开与行内编辑一张表里的两种渲染态这是该示例最具高级感的部分同一张表在不同行上同时存在展示态与编辑态。行展开行首展开按钮调用row.toggleExpanded()展开后在行下方插入一行colSpan横跨所有列的只读Textarea展示该帖子的content全文{row.getIsExpanded() ( Tr idexpanded-row Td colSpan{row.getVisibleCells().length} Textarea readOnly value{row.original.content} / /Td /Tr )}行内编辑通过id (row.original as IPost).id判断当前行是否为编辑目标行若是则调用renderEditRow(row)渲染编辑态行。编辑态行包含title、status、category.id三个受控输入通过register(...)注册到react-hook-form以及保存/取消按钮const renderEditRow useCallback( (row: RowIPost) { const { id } row.original; return ( React.Fragment key{id} Tr {/* ... */} Td Input idtitle-input {...register(title)} / /Td Td Select {...register(status)} option valuepublishedpublished/option option valuedraftdraft/option option valuerejectedrejected/option /Select /Td Td Select {...register(category.id)} {options.map((item) ( option key{item.value} value{Number(item.value)} {item.label} /option ))} /Select /Td {/* Save / Cancel 按钮 */} /Tr Tr Td colSpan{getAllColumns().length} Textarea {...register(content)} / /Td /Tr /React.Fragment ); }, [options], );整个表格被包裹在一个form onSubmit{handleSubmit(onFinish)}中保存按钮即表单提交按钮提交后由useForm的onFinish触发更新请求PATCH /posts/{id}随后setId被清空、该行回归展示态。分类下拉选项由useSelect提供resource: categoriespageSize: 9999一次性拉取全部mode: server服务端分页模式其数据结构{ value, label }与option直接对应。关联数据预取useMany 与 setOptions 的配合由于category.id列需要展示分类名称示例通过两条路径获取分类数据当前页关联数据从表格数据中收集当前页涉及的所有category.id调用useMany批量查询const categoryIds tableData?.data?.map((item) item.category.id) ?? []; const { result: categoriesData } useManyICategory({ resource: categories, ids: categoryIds, queryOptions: { enabled: categoryIds.length 0 }, });注入表格 meta通过setOptions将查询结果塞回 TanStack 表格的meta供 category 列的单元格渲染函数读取setOptions((prev) ({ ...prev, meta: { ...prev.meta, categoriesData, }, }));这种做法把关联数据解析从渲染闭包中解耦table.options.meta.categoriesData成为列内共享的数据通道。同时编辑态行使用的分类选项则来自useSelect二者分工明确useMany负责展示解析useSelect负责编辑选择。分页usePagination 驱动的页码条示例没有直接使用 TanStack 的默认分页 UI而是基于refinedev/chakra-ui的usePagination封装了自定义分页条 src/components/pagination/index.tsxconst pagination usePagination({ current, pageCount });current、pageCount、setCurrent均来自useTable解构出的refineCore字段currentPage别名currentsetCurrentPage别名setCurrent。组件渲染上一页 / 页码含省略号.../ 下一页结构页码点击调用setCurrent(page)触发 Refine 重新请求对应页数据。{pagination?.items.map((page) { if (typeof page string) return span key{page}.../span; return ( Button key{page} onClick{() setCurrent(page)} variant{page current ? solid : outline} {page} /Button ); })}pagination.prev/pagination.next为布尔值控制首尾翻页按钮是否渲染从而在首页/末页自动隐藏不可用的翻页方向。端到端测试功能行为的验证依据该示例的交互行为有 Cypress 端到端测试背书见 cypress/e2e/table-chakra-ui-advanced/all.cy.ts覆盖了前文描述的四大核心能力行展开点击.tabler-icon-chevron-right后断言#expanded-row出现全选点击表头Checkbox后断言所有#row-select勾选框均被选中批量删除按钮显隐初始断言#delete-selected不存在勾选任意一行后断言其出现行内编辑点击编辑按钮后断言表单被当前行数据填充#title-input的值为接口返回的title修改并保存后断言PATCH请求体中的title已更新。这些测试与 list.tsx 中#expanded-row、#delete-selected、#row-select、#title-input等 DOM 标识一一对应可以作为你自行扩展表格功能时的回归测试模板。运行方式在本地运行该示例源自 examples/table-chakra-ui-advanced/README.mdnpm create refine-applatest -- --example table-chakra-ui-advanced进入项目目录后执行pnpm install仓库使用 pnpm 管理依赖再运行pnpm dev即可启动开发服务器随后访问/posts页面体验点击列头排序图标切换升降序、点击筛选图标对 Title 做包含匹配或对 Status 做下拉精确匹配、勾选多行批量删除、点击行首箭头展开全文、点击行内 Edit 就地修改并保存。整个过程的网络请求会以带查询参数的 REST 请求形式打到https://api.fake-rest.refine.dev/posts便于在浏览器 DevTools 中直观理解 Refine 表格状态如何被翻译为后端查询。小结Chakra UI Advanced Table 示例是 Refine v5 表格能力的浓缩展示refinedev/react-table负责把 TanStack React Table 的状态模型与 Refine 的数据层无缝桥接refinedev/react-hook-form让行内编辑与 Refine 的 mutation 流程对接useMany/useSelect解决关联数据展示与编辑选项而usePagination、ColumnFilter、ColumnSorter则提供了可复用的交互基建。当你需要在一个真实管理后台中同时满足筛选、排序、多选删除、行内编辑、展开详情这些需求时本文描述的这套组件结构与 hooks 组合可以直接迁移到自己的 Refine 项目中。【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考