Refine v5 中 Ant Design DeleteButton 的完整指南:确认弹窗、useDelete 调用链与权限控制
2026/9/13 10:25:12 网站建设 项目流程

Refine v5 中 Ant Design DeleteButton 的完整指南:确认弹窗、useDelete 调用链与权限控制

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

<DeleteButton>是 Refine 基于 Ant Design 二次封装的危险操作按钮组件,专门用于删除场景。它集成了 Ant Design 的<Button><Popconfirm>,点击后会弹出确认框,确认后才通过 dataProvider 的deleteOne方法真正执行删除——这种"先确认、后执行"的设计能有效防止误删。本文围绕该组件的核心脉络展开:先看它在表格中的典型用法,再逐一拆解recordItemIdresourceonSuccessmutationModehideTextaccessControl等关键属性,最后深入源码与测试,讲清确认弹窗文案、useDeleteButton内部调用链以及 i18n 机制,让你在管理后台中安全、优雅地实现删除功能。

组件概览:基于<Button><Popconfirm>的组合

根据官方文档(documentation/docs/ui-integrations/ant-design/components/buttons/delete-button/index.md),<DeleteButton>底层由 Ant Design 的<Button><Popconfirm>两个组件组合而成:

  • 当你尝试删除某条数据时,页面上会弹出确认气泡(Popconfirm),询问是否确认删除;
  • 点击确认后,组件会执行由 dataProvider 提供的useDelete方法,真正发起删除请求。

这一行为在源码中得到了完全印证。打开 packages/antd/src/components/buttons/delete/index.tsx,可以看到组件的渲染结构:

return ( <Popconfirm key="delete" okText={confirmOkText ?? defaultConfirmOkLabel} cancelText={confirmCancelText ?? defaultCancelLabel} okType="danger" title={confirmTitle ?? defaultConfirmTitle} okButtonProps={{ disabled: loading }} onConfirm={onConfirm} disabled={isDisabled} > <Button danger loading={loading} icon={<DeleteOutlined />} title={title} disabled={isDisabled} >import { List, useTable, DeleteButton, } from "@refinedev/antd"; import { Table } from "antd"; const PostList = () => { const { tableProps } = useTable<IPost>(); return ( <List> <Table {...tableProps} rowKey="id"> <Table.Column dataIndex="id" title="ID" /> <Table.Column dataIndex="title" title="Title" width="50%" /> <Table.Column<IPost> title="Actions" dataIndex="actions" key="actions" render={(_, record) => ( <DeleteButton size="small" recordItemId={record.id} /> )} width="50%" /> </Table> </List> ); }; interface IPost { id: number; title: string; }

关键点在于recordItemId={record.id}——它把当前行的记录 ID 绑定到删除按钮上。点击按钮后,组件会自动推断资源名与记录 ID,触发useDelete,从而删除对应记录。文档中的 live 示例还展示了该页面挂载在/posts路由下,资源名定义为posts

核心属性详解

recordItemId:指定要删除哪条记录

recordItemId用于管理将要删除的记录。默认情况下,它会从路由参数中自动推断(例如在posts/edit/123页面中,123会自动成为删除目标),因此在编辑/详情页内放置删除按钮时通常可以省略。

在列表页这类无法从路由推断 ID 的场景,则需要显式传入:

import { DeleteButton } from "@refinedev/antd"; const MyDeleteComponent = () => { return ( <DeleteButton resource="posts" recordItemId="123" /> ); };

点击按钮后,useDelete会删除资源为posts、ID 为123的记录。注意这里recordItemId传入的是字符串"123",而BaseKey类型同样支持数字等类型,具体以你的主键类型为准。

resource:指定删除哪个资源的记录

resource用于管理将要删除的是哪个资源的记录,默认同样从路由参数推断。当需要删除其他资源的数据(例如在 posts 页面里删除 categories 数据)时,显式传入资源名:

import { DeleteButton } from "@refinedev/antd"; const MyDeleteComponent = () => { return <DeleteButton resource="categories" recordItemId="123" />; };

点击后,useDelete会删除资源为categories、ID 为123的记录。

同名资源的处理(identifier):如果你在<Refine/>中定义了多个同名的资源,可以传入identifier而非name来区分。identifier只作为资源的主匹配键,dataProvider 的方法仍然使用<Refine/>组件中定义的资源name来执行。更详细的说明可参考 documentation/docs/core/refine-component/index.md 中的identifier小节。

onSuccess:删除成功后的回调

onSuccess允许你在删除请求返回结果后做额外处理,例如打日志、刷新统计、跳转等。文档示例中在删除成功后console.log返回值:

import { DeleteButton } from "@refinedev/antd"; const MyDeleteComponent = () => { return ( <DeleteButton resource="posts" recordItemId="1" onSuccess={(value) => { console.log(value); }} /> ); };

文档配套的 live 示例还演示了一个自定义 dataProvider:通过覆盖deleteOne方法模拟 500ms 延迟后返回{ message: "You have successfully deleted the record" },此时onSuccess收到的value就是这个自定义返回对象。这说明onSuccess拿到的是 dataProviderdeleteOne的返回值(即DeleteOneResponse),因此你可以利用它拿到服务端返回的业务信息,而不只是"请求成功"这个状态。

mutationMode:删除的三种提交模式

mutationMode决定删除操作的提交方式,可选值为:

  • pessimistic(默认):先调用 dataProvider,成功后再更新 UI,最安全;
  • optimistic:先更新 UI,再后台调用 dataProvider,响应更快但失败时需回滚;
  • undoable:先更新 UI,同时在指定时间内提供撤销入口,超时后才真正调用 dataProvider。

在表格中为单个按钮设置示例:

import { List, DeleteButton, useTable } from "@refinedev/antd"; import { Table } from "antd"; 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.Column<IPost> title="Actions" dataIndex="actions" render={(_, record) => ( <DeleteButton size="small" recordItemId={record.id} mutationMode="undoable" /> )} /> </Table> </List> ); };

对删除这类危险操作,推荐优先使用默认的pessimisticundoable适合希望给用户反悔机会的场景(删除后出现"撤销"提示)。更完整的模式对比与配置说明见 documentation/docs/advanced-tutorials/mutation-mode.md。

hideText:只显示图标

hideText用于控制是否显示按钮文字。设为true时,按钮只保留图标(DeleteOutlined),适合操作列空间紧张的场景:

import { DeleteButton } from "@refinedev/antd"; const MyDeleteComponent = () => { return ( <DeleteButton hideText={true} recordItemId="123" /> ); };

从源码可以看到,hideText的默认值为false;且当传入children时,会优先渲染children作为按钮内容(children ?? label)。

accessControl:接入权限控制

accessControl属性用于控制删除按钮的权限表现,仅在向<Refine/>提供了accessControlProvider时生效。它支持两个子属性:

  • enabled:是否启用访问控制检查(默认组件本身会启用,可显式控制);
  • hideIfUnauthorized:用户无权限时是否直接隐藏按钮。
import { DeleteButton } from "@refinedev/antd"; export const MyListComponent = () => { return ( <DeleteButton accessControl={{ enabled: true, hideIfUnauthorized: true, }} /> ); };

若不设置hideIfUnauthorized,无权限时按钮通常表现为禁用而不是隐藏。完整的accessControlProvider定义可参考 documentation/docs/authorization/access-control-provider/index.md。

API Reference 与外部属性

该组件的属性完整定义见文档中的Properties表格(@refinedev/antd/DeleteButton)。此外,它接受 Ant Design<Button>的全部 props(如sizedangericonloading等),这意味着你可以完全按 Ant Design 的按钮规范自由定制外观与行为。

源码探秘:useDeleteButton 的调用链

<DeleteButton>本身是纯展示层,真正的逻辑集中在 Refine Core 提供的useDeleteButton钩子中。它的实现位于 packages/core/src/hooks/button/delete-button/index.tsx,核心流程如下:

export function useDeleteButton(props: DeleteButtonProps): DeleteButtonValues { const translate = useTranslate(); const { mutation: { mutate, isPending, variables }, } = useDelete(); const { setWarnWhen } = useWarnAboutChange(); const { mutationMode } = useMutationMode(props.mutationMode); const { id, resource, identifier } = useResourceParams({ resource: props.resource, id: props.id, }); const { title, disabled, hidden, canAccess } = useButtonCanAccess({ action: "delete", accessControl: props.accessControl, meta: props.meta, id, resource, }); const label = translate("buttons.delete", "Delete"); const confirmOkLabel = translate("buttons.delete", "Delete"); const confirmTitle = translate("buttons.confirm", "Are you sure?"); const cancelLabel = translate("buttons.cancel", "Cancel"); const loading = id === variables?.id && isPending; const onConfirm = () => { if (id && identifier) { setWarnWhen(false); mutate( { id, resource: identifier, mutationMode, successNotification: props.successNotification, errorNotification: props.errorNotification, meta: props.meta, dataProviderName: props.dataProviderName, invalidates: props.invalidates, }, { onSuccess: props.onSuccess, }, ); } }; return { label, title, hidden, disabled, canAccess, loading, confirmOkLabel, cancelLabel, confirmTitle, onConfirm, }; }

从源码可以提炼出以下关键事实:

  1. 参数兜底useResourceParams会解析resourceid,当组件未显式传入时,从当前路由上下文自动推断(这正是文档所说"默认从路由参数推断"的底层实现)。useDeleteButton同时返回resourceidentifier,后者用于在mutate时作为resource参数传递。
  2. 确认框文案走 i18n:按钮文字(buttons.delete)、确认标题(buttons.confirm→ "Are you sure?")、确认/取消按钮文字(buttons.delete/buttons.cancel)全部通过useTranslate读取;未命中时使用内置英文默认值。
  3. 删除走useDelete:确认后调用useDeletemutate,并携带mutationModesuccessNotificationerrorNotificationmetadataProviderNameinvalidates等参数,onSuccess作为 mutation 回调传入。useDelete的完整说明见 documentation/docs/data/hooks/use-delete/index.md,其底层通过 dataProvider 的deleteOne方法发起请求(dataProvider 接口定义见 documentation/docs/guides-concepts/data-fetching/data-provider-interface.md)。
  4. loading 状态loading = id === variables?.id && isPending——只有当当前按钮的id恰好等于正在执行中的删除请求的id时,该按钮才进入 loading 状态,避免列表中其他行的按钮被"连带转圈"。
  5. 权限与表单联动useButtonCanAccess负责访问控制检查,setWarnWhen(false)则在删除前重置"未保存更改"警告(useWarnAboutChange),防止删除操作被表单未保存提示拦截。

确认框文本的 i18n 测试佐证

useDeleteButton的测试位于 packages/core/src/hooks/button/delete-button/index.spec.tsx,其中两个用例直接验证了文案机制:

  • 默认(无 i18nProvider)时返回label: "Delete"confirmOkLabel: "Delete"confirmTitle: "Are you sure?"cancelLabel: "Cancel"
  • 传入自定义i18nProvider后,buttons.deletebuttons.confirmbuttons.cancel三个 key 会被翻译成自定义文案(如 "Delete (i18n)")。

也就是说,只需在 i18n 资源中覆盖buttons.delete/buttons.confirm/buttons.cancel,即可全局本地化删除按钮与确认框的文字。

组件的单元测试入口

Ant Design 侧对<DeleteButton>的测试通过复用@refinedev/ui-tests的通用按钮测试套件完成(见 packages/antd/src/components/buttons/delete/index.spec.tsx):

import { buttonDeleteTests } from "@refinedev/ui-tests"; import { DeleteButton } from "./"; describe("Delete Button", () => { buttonDeleteTests.bind(this)(DeleteButton); });

这意味着删除按钮的渲染、确认交互、禁用/隐藏、权限等行为都由统一的测试契约保障,也侧面印证了各 UI 框架(Ant Design、MUI、Mantine、Chakra UI)的删除按钮行为一致。

配套能力:批量删除与相关参考

批量删除场景

当需要支持表格多选批量删除时,文档建议使用useDeleteMany而非逐行<DeleteButton>。仓库示例 examples/table-antd-use-delete-many/src/pages/posts/list.tsx 展示了完整做法:通过rowSelection收集选中的行,点击 "Delete Selected" 按钮后调用useDeleteMany一次性删除多个 ID,成功后清空选中态:

const { mutate, mutation: { isPending: deleteManyIsLoading }, } = useDeleteMany<IPost>(); const deleteSelectedItems = () => { mutate( { resource: "posts", ids: selectedRowKeys.map(String), }, { onSuccess: () => { setSelectedRowKeys([]); }, }, ); };

该示例同时展示了把EditButtonShowButton等与删除按钮组合放进操作列(搭配hideText)的常见布局。

快速自定义:使用 Refine CLI 进行 Swizzle

文档特别提示:你可以通过Refine CLI对这个组件进行 swizzle(把组件源码"弹出"到你的项目中)以便深度自定义。Refine CLI 的安装与使用方法见 documentation/docs/packages/cli/index.md。swizzle 之后,你可以直接修改弹出副本的 JSX 结构、样式或默认行为,同时保留原有 props 接口。

小结

<DeleteButton>把"危险操作需二次确认"这一最佳实践固化成了开箱即用的组件:外层由 Ant Design 的Popconfirm兜底确认交互,内层逻辑由useDeleteButton串联useDelete→ dataProviderdeleteOne的完整调用链,并天然支持路由参数推断、i18n 文案、访问控制和三种 mutation 模式。日常使用中只需记住几个要点:列表页记得传recordItemId,跨资源操作记得传resource,需要权限控制时配置accessControlProvider并结合hideIfUnauthorized,追求极简布局时用hideText。若需要批量删除,则切换到useDeleteMany;若要深度定制组件本身,可用 Refine CLI 进行 swizzle。

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

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

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

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

立即咨询