如何在 Refine Ant Design 中配合 useEditableTable 实现表格内行内编辑?
【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards & B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine
如果你的 Ant Design 列表页目前只能整页跳转去编辑,而希望用户直接在表格行内修改某个字段并保存,Refine 的@refinedev/antd包提供了useEditableTablehook 来完成这件事。它由useTable扩展而来,返回可以直接铺到 Ant Design<Table>和<Form>上的属性,以及控制“哪一行正在编辑”的一组按钮属性和方法。本文以官方示例工程table-antd-use-editable-table的帖子(posts)列表为例,走通从项目创建、页面改造到保存验证的完整路径。
准备条件:获取示例工程
仓库中的完整示例位于 examples/table-antd-use-editable-table,README 给出的本地获取命令是:
npm create refine-app@latest -- --example table-antd-use-editable-table该示例的依赖版本以工程 package.json 为准:@refinedev/antd^6.0.3、antd^5.23.0、react^19.1.0,且engines要求node >= 20。数据层使用@refinedev/simple-rest,接口地址在 App.tsx 中配置:
const API_URL = "https://api.fake-rest.refine.dev"; <Refine dataProvider={dataProvider(API_URL)} routerProvider={routerProvider} resources={[{ name: "posts", list: "/posts" }]} notificationProvider={useNotificationProvider} options={{ syncWithLocation: true, warnWhenUnsavedChanges: true }} >其中notificationProvider={useNotificationProvider}来自@refinedev/antd,用于在请求后弹出成功/失败提示;resources中的list: "/posts"决定了列表页路由,路由部分由 App.tsx 中的<Route path="/posts" element={<PostList />} />挂载。
如果你的项目已有 Refine 应用且数据来自其他接口,保留自己现有的dataProvider即可,本文后续的页面代码只依赖列表数据有唯一id字段。示例的数据结构定义在 interfaces/index.d.ts:
export interface IPost { id: number; title: string; }第一步:用 Form 包住 Table
useEditableTable的核心用法是:调用 hook 拿到tableProps和formProps,把<Table>用<Form>包住,并给表格指定rowKey="id"(文档示例中rowKey固定为id,对应数据记录的主键字段名,你的字段名不同就改成实际主键字段):
import { List, useEditableTable } from "@refinedev/antd"; import { Table, Form } from "antd"; export const PostList: React.FC = () => { const { tableProps, formProps } = useEditableTable<IPost>(); return ( <List> <Form {...formProps}> <Table {...tableProps} rowKey="id"> <Table.Column dataIndex="id" title="ID" /> <Table.Column dataIndex="title" title="Title" /> </Table> </Form> </List> ); }; interface IPost { id: number; title: string; }这一步只是把表格接上了行内编辑的表单基础设施,此时任何单元格都还不可编辑。
第二步:给每行加“编辑 / 保存 / 取消”操作列
从 hook 中再解构出isEditing、saveButtonProps、cancelButtonProps、editButtonProps,然后新增一个 Actions 列。判断逻辑是:isEditing(record.id)返回true时说明该行正处于编辑态,展示 Save 和 Cancel 按钮;否则展示 Edit 按钮:
const { tableProps, formProps, isEditing, saveButtonProps, cancelButtonProps, editButtonProps, } = useEditableTable<IPost>();<Table.Column<IPost> title="Actions" dataIndex="actions" key="actions" render={(_text, record) => { if (isEditing(record.id)) { return ( <Space> <SaveButton {...saveButtonProps} size="small" /> <Button {...cancelButtonProps} size="small"> Cancel </Button> </Space> ); } return ( <Space> <EditButton {...editButtonProps(record.id)} size="small" /> </Space> ); }} />三个按钮属性都来自 hook 返回:editButtonProps接收行id作为参数,点击时会让该行进入编辑态;saveButtonProps是提交按钮的 props;cancelButtonProps的onClick会被useEditableTable覆盖为把编辑 id 重置为undefined,即退出编辑态。SaveButton、EditButton、List均从@refinedev/antd导入,Space、Button从antd导入。
第三步:让目标列在编辑态下显示输入框
默认情况下即使进入了编辑态,列里的值也只是普通文本。要让某列可编辑,需要给<Table.Column>提供render:当isEditing(record.id)为true时返回一个带name的<Form.Item>包裹antd的<Input>,否则渲染只读值。这就是文档所说的“条件渲染”,Form.Item的name要与可编辑字段一致:
<Table.Column<IPost> key="title" dataIndex="title" title="Title" render={(value, record) => { if (isEditing(record.id)) { return ( <Form.Item name="title" style={{ margin: 0 }}> <Input /> </Form.Item> ); } return <TextField value={value} />; }} />按文档的说明:用户点击编辑按钮后,isEditing(lineId)对相关行变为true,正在编辑的行上出现输入框;编辑完成后点击 SaveButton 即可保存新值。TextField来自@refinedev/antd,非编辑态时用于展示原始值。
可选分支:点击行直接进入编辑态
除了按钮触发,文档给出了另一种方式:利用<Table>的onRow属性,在行被点击时调用useEditableTable返回的setId把该行置为编辑态:
const { tableProps, formProps, isEditing, setId } = useEditableTable<IPost>(); <Table {...tableProps} rowKey="id" onRow={(record) => ({ onClick: (event: any) => { if (event.target.nodeName === "TD") { setId && setId(record.id); } }, })} >event.target.nodeName === "TD"这个判断保留了文档原样:只有点击落在单元格上才触发进入编辑,点击行内的其他元素(比如按钮)不会触发。两种方式可以并存,也可以只用其中一种。
完整实现
官方示例工程中的最终页面在 src/pages/posts/list.tsx,把上述按钮、可编辑列和点行编辑组合在了一起:
import { List, SaveButton, EditButton, TextField, useEditableTable, } from "@refinedev/antd"; import { Table, Form, Space, Button, Input } from "antd"; import type { IPost } from "../../interfaces"; export const PostList = () => { const { tableProps, formProps, isEditing, setId: setEditId, saveButtonProps, cancelButtonProps, editButtonProps, } = useEditableTable<IPost>(); return ( <List> <Form {...formProps}> <Table {...tableProps} rowKey="id" onRow={(record) => ({ // eslint-disable-next-line onClick: (event: any) => { if (event.target.nodeName === "TD") { setEditId?.(record.id); } }, })} > <Table.Column dataIndex="id" title="ID" /> <Table.Column<IPost> dataIndex="title" title="Title" render={(value, record) => { if (isEditing(record.id)) { return ( <Form.Item name="title" style={{ margin: 0 }}> <Input /> </Form.Item> ); } return <TextField value={value} />; }} /> <Table.Column<IPost> title="Actions" dataIndex="actions" render={(_, record) => { if (isEditing(record.id)) { return ( <Space> <SaveButton {...saveButtonProps} hideText size="small" /> <Button {...cancelButtonProps} size="small"> Cancel </Button> </Space> ); } return ( <EditButton {...editButtonProps(record.id)} hideText size="small" /> ); }} /> </Table> </Form> </List> ); };运行与结果验证
在示例工程根目录执行npm run dev(对应脚本为refine dev),访问/posts路由打开帖子列表,按文档描述的行为逐项核对:
- 点击某行的 Edit 按钮(或直接点击该行任意单元格,
nodeName为TD时)后,该行的 Title 列从只读文本切换为输入框,Actions 列切换为 Save 和 Cancel——这说明isEditing(record.id)已对该行返回true; - 修改标题后点击 Save 按钮,提交成功后行内编辑关闭、表格展示新值;文档指出这由
autoSubmitClose行为保证:该选项默认为true,useEditableTable会在提交成功后自动以undefined调用setId,让该行退出编辑态。若希望保存后仍停留在编辑态,可在 hook 中显式关闭:
const editableTable = useEditableTable({ autoSubmitClose: false, });- 点击 Cancel 按钮,该行退出编辑态且不提交改动(
cancelButtonProps的默认行为是把编辑 id 重置为undefined)。
提交状态本身也可以通过 hook 返回的mutation(表单提交触发的 mutation 结果)和formLoading(表单请求加载态)在自定义 UI 中消费;示例工程则通过useNotificationProvider统一弹出请求结果提示。
限制与可参考的文档
useEditableTable继承useTable,因此useForm与useTable的全部属性和返回值在useEditableTable中都可用(如searchFormProps、tableQuery、sorter、filters、query等),需要排序、筛选或搜索参数时可直接沿用,无需换成useTable;- 文档中
autoSubmitClose的默认值是true;isEditing、setId、saveButtonProps、cancelButtonProps、editButtonProps的返回签名见 useEditableTable hook 文档 的 “Return values” 表格; - 该示例的在线预览入口(CodeSandboxExample,工程路径
table-antd-use-editable-table)在 示例文档页 中可直接对照运行效果。
【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards & B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考