react-admin 修订历史体系(ra-history)中的 useDeleteRevisions:彻底清除记录修订数据的正确姿势

原创2026-09-21 13:44:541,642 阅读
文章标签:前端UI组件

react-admin 修订历史体系(ra-history)中的 useDeleteRevisions:彻底清除记录修订数据的正确姿势

导读

useDeleteRevisions 是 react-admin 企业版扩展包 @react-admin/ra-core-ee(ra-history 功能族)提供的一个 mutation Hook,用于删除某条记录的全部修订版本(revisions)。它的典型应用场景是:当一条记录本身被删除时,同步清空它遗留在修订表中的历史数据,避免脏数据与孤儿修订长期堆积。读完本文,你将掌握 useDeleteRevisions 的完整 API(参数、返回值、覆盖策略),学会在删除按钮的 mutation 回调中优雅地联动删除修订,并理解它与 useGetRevisions、useAddRevisionAfterMutation 以及 RevisionsButton 组件共同构成的"记录历史追踪"闭环。

一、useDeleteRevisions 是什么

useDeleteRevisions 的核心职责非常聚焦:为指定记录触发一次"删除全部修订"的 mutation。官方对其描述是:

Provides a mutation function to delete all revisions for a specific record. This is useful notably to delete all revisions when the record itself is deleted.

即:它并不负责删除记录本身,而是负责删除与某条记录关联的全部修订数据。最典型的业务组合是"删除记录 + 删除该记录的修订",这正是官方文档示例 <DeleteWithRevisionsButton> 所演示的场景。

需要明确的是,该 Hook 属于 Enterprise Edition(企业版) 功能,要求有效的企业版订阅,并随私有 npm 仓库中的 @react-admin/ra-core-ee 包发布(见 HistoryFeatures.md 的安装说明)。

二、使用前置:让 dataProvider 支持修订能力

在使用任何修订相关 Hook 之前,必须先在 dataProvider 上补齐三个修订方法,ra-history 的一切查询与写入都建立在它们之上:

  • getRevisions:读取某条记录的修订列表;
  • addRevision:为某条记录新增一条修订;
  • deleteRevisions:删除某条记录的全部修订(useDeleteRevisions 底层正是调用它)。
const dataProviderWithRevisions = {
    ...dataProvider,
    getRevisions: async (resource, params) => {
        const { recordId } = params;
        // ...
        return { data: revisions };
    },
    addRevision: async (resource, params) => {
        const { recordId, data, authorId, message, description } = params;
        // ...
        return { data: revision };
    },
    deleteRevisions: async resource => {
        const { recordId } = params;
        // ...
        return { data: deletedRevisionIds };
    },
};

需要注意,修订(revision)对象是**不可变(immutable)**的,因此不需要实现 updateRevision 方法。一条修订的典型结构如下:

{
    id: 123, // 修订 id
    resource: 'products', // 资源名
    recordId: 456, // 记录 id
    data: {
        id: 456,
        title: 'Lorem ipsum',
        teaser: 'Lorem ipsum dolor sit amet',
        body: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit',
    }, // 记录当时的数据快照
    // 元数据
    authorId: 789, // 修订作者 id
    date: '2021-10-01T00:00:00.000Z', // 修订时间
    message: 'Updated title, teaser, body', // 提交信息
    description: 'Added a teaser', // 提交描述
}

如果你不想手写这三个方法,ra-core-ee 还提供了两个生成器(builder):

  • addRevisionMethodsBasedOnSingleResource:把所有资源的修订集中存放到一个 revisions 资源中;
  • addRevisionMethodsBasedOnRelatedResource:把每个资源的修订存放在相关联的资源中(例如 products 的修订放在 products_history)。

配置好之后,把增强后的 dataProvider 传给 <CoreAdmin> 即可启用整套历史功能,具体安装与配置细节见 HistoryFeatures.md。

三、Hook 用法:删除记录并同步删除修订

官方文档给出了一个完整的实战示例:实现 <DeleteWithRevisionsButton>,在成功删除记录后,紧接着删除该记录的全部修订。

import { EditBase, Form, useResourceContext, useRecordContext } from 'ra-core';
import { TextInput, DeleteButton } from 'my-react-admin-ui-library';
import { useDeleteRevisions } from '@react-admin/ra-core-ee';

const DeleteWithRevisionsButton = () => {
    const resource = useResourceContext();
    const record = useRecordContext();
    const [deleteRevisions] = useDeleteRevisions();
    return (
        <DeleteButton
            mutationOptions={{
                onSettled: (_data, error) => {
                    if (error) return;
                    deleteRevisions(resource, { recordId: record?.id });
                },
            }}
            label="Delete with revisions"
        />
    );
};

export const ProductEdit = () => (
    <EditBase>
        <Form>
            <TextInput source="reference" />
            <TextInput source="category" />
            <DeleteWithRevisionsButton />
        </Form>
    </EditBase>
);

3.1 关键设计点解析

为什么用 onSettled 而不是 onSuccess?

DeleteButton 的 mutationOptions.onSettled 在 mutation 结束后一定会被调用(无论成功或失败)。示例中先判断 error,仅当没有错误时才触发 deleteRevisions,从而保证:只有记录确实删除成功,才清理其修订。如果删除失败,则保留修订,避免"记录还在、修订没了"或重复清理的竞态问题。

resource 与 recordId 从哪来?

  • resource 通过 useResourceContext() 从当前的 ResourceContext 获取;
  • recordId 通过 useRecordContext() 从当前记录的 RecordContext 获取,即 record?.id。

这两个 Context 在 <EditBase> 包裹的页面内自动注入,因此按钮组件无需自行接收 props。

deleteRevisions 是 Hook 返回的 mutation 触发函数,调用签名 deleteRevisions(resource, { recordId }) 明确告诉 Hook 要清理哪个资源、哪条记录的修订。

四、完整 API 参考

4.1 Hook 参数(hook-time)

参数 类型 说明
resource? string 资源名,默认取自当前资源上下文(ResourceContext)
params? object 默认参数,必须包含 recordId 属性
options? object 默认的 React Query mutation options

4.2 返回值

返回一个元组(tuple),与 react-admin 其他 mutation Hook 的惯例一致:

  1. deleteRevisions:触发删除的函数;
  2. mutation:React Query 的 mutation 结果对象(包含 data、error、isPending、isSuccess 等状态属性,可用于渲染加载态或错误提示)。

4.3 deleteRevisions 调用时参数(call-time,可覆盖)

调用 deleteRevisions 时传入的参数会覆盖 Hook 阶段设置的默认值:

参数 类型 说明
resource? string 资源名,覆盖 Hook 阶段的 resource
params? object 含 recordId 的对象,覆盖 Hook 阶段的 params
options? object mutation options,支持 returnPromise: boolean,覆盖 Hook 阶段的 options

其中 returnPromise 是值得特别说明的选项:当设为 true 时,deleteRevisions 会返回一个 Promise,便于调用方 await 等待删除完成后再执行后续逻辑(例如在删除修订成功后再触发导航、刷新列表或弹通知)。这在需要严格串行编排"删记录 → 删修订 → 后续动作"的场景中非常有用。

五、在 react-admin 历史功能族中的定位

useDeleteRevisions 并不是孤立存在的,它属于 ra-core-ee 提供的"历史追踪(History)"能力体系。从仓库文档的目录结构(astro.config.mjs 中列出了 HistoryFeatures 与 useDeleteRevisions 等条目)可以看到,完整的修订工作流由以下成员协作完成:

  • useGetRevisions:查询某条记录的修订列表,返回 React Query 查询结果(data、isPending、error、refetch 等),用于渲染修订历史面板。详见 useGetRevisions.md。
  • useAddRevisionAfterMutation:注册一个 mutation 中间件,在创建/更新成功后自动为记录生成一条修订;修订元数据通过 REVISION_FIELD 常量写入表单数据,保存前会被中间件剥离。详见 useAddRevisionAfterMutation.md。
  • useGenerateChangeMessage:对比新数据与现有记录,自动生成人类可读的变更描述(如 "Changed name, description"、"Initial revision"、"No changes"),并已通过 i18n 国际化(翻译键为 ra-history.on_save.*)。详见 useGenerateChangeMessage.md。
  • <RevisionsButton>(在 docs/RevisionsButton.md 中):UI 层组件,读取当前记录与资源上下文,调用 dataProvider.getRevisions() 展示修订列表、diff 对比,并支持 allowRevert 回滚到历史版本,通常配合 <SimpleFormWithRevision> 在编辑页的 actions 中使用。

据此可以勾勒出一条完整的修订生命周期:

  1. 写入:useAddRevisionAfterMutation + useGenerateChangeMessage 在每次保存时创建修订;
  2. 读取/回滚:useGetRevisions 与 <RevisionsButton> 展示历史、对比差异、支持回滚;
  3. 清理:useDeleteRevisions 在记录删除时彻底清除历史,完成生命周期的收尾。

useDeleteRevisions 正是这条链路中的"终点清理"环节——它确保删除操作不留下孤儿修订数据,保持修订表的整洁,也避免之后误把已删除记录的修订当作有效数据展示。

六、最佳实践与注意事项

  1. 先删记录、确认成功后再删修订:借助 DeleteButton 的 onSettled 回调判断 error,保证修订清理只发生在记录删除成功后。
  2. 善用 call-time 覆盖:如果同一页面需要在不同资源间切换清理,可以在调用时显式传入 resource 与 params,而不必依赖 Hook 阶段的默认值。
  3. 串行编排用 returnPromise:需要在修订删除完成后做后续处理(如提示、刷新、跳转)时,将 returnPromise: true 传入 options 并 await。
  4. 展示层联动:删除后若页面还挂着修订面板(如 RevisionsButton 的 diff 视图),应同步刷新或关闭,避免展示已删除记录的修订;配合 useGetRevisions 的 refetch 或查询失效机制处理即可。
  5. 企业版前提:useDeleteRevisions 位于 @react-admin/ra-core-ee,需要企业版订阅才能从私有 npm 仓库安装使用;公共开源版本的 react-admin 不包含该 Hook,相关能力参考 HistoryFeatures.md 完成订阅与配置。

小结

useDeleteRevisions 以极简的 API(一个 mutation 函数 + 一个 mutation 结果对象)解决了"记录删除后修订数据清理"这一真实痛点。掌握它的 Hook 参数、返回值与 call-time 覆盖规则,配合 dataProvider 的三个修订方法与 onSettled 回调的时序控制,你就可以在自己的 react-admin 企业版应用中安全、完整地实现"删除记录并清除其全部历史修订"的闭环,让修订数据生命周期管理既可靠又可维护。

登录后查看全文
react-admin