react-admin 修订历史体系(ra-history)中的 useDeleteRevisions:彻底清除记录修订数据的正确姿势
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 的惯例一致:
deleteRevisions:触发删除的函数;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中使用。
据此可以勾勒出一条完整的修订生命周期:
- 写入:
useAddRevisionAfterMutation+useGenerateChangeMessage在每次保存时创建修订; - 读取/回滚:
useGetRevisions与<RevisionsButton>展示历史、对比差异、支持回滚; - 清理:
useDeleteRevisions在记录删除时彻底清除历史,完成生命周期的收尾。
useDeleteRevisions 正是这条链路中的"终点清理"环节——它确保删除操作不留下孤儿修订数据,保持修订表的整洁,也避免之后误把已删除记录的修订当作有效数据展示。
六、最佳实践与注意事项
- 先删记录、确认成功后再删修订:借助
DeleteButton的onSettled回调判断error,保证修订清理只发生在记录删除成功后。 - 善用 call-time 覆盖:如果同一页面需要在不同资源间切换清理,可以在调用时显式传入
resource与params,而不必依赖 Hook 阶段的默认值。 - 串行编排用
returnPromise:需要在修订删除完成后做后续处理(如提示、刷新、跳转)时,将returnPromise: true传入 options 并await。 - 展示层联动:删除后若页面还挂着修订面板(如
RevisionsButton的 diff 视图),应同步刷新或关闭,避免展示已删除记录的修订;配合useGetRevisions的refetch或查询失效机制处理即可。 - 企业版前提:
useDeleteRevisions位于@react-admin/ra-core-ee,需要企业版订阅才能从私有 npm 仓库安装使用;公共开源版本的 react-admin 不包含该 Hook,相关能力参考 HistoryFeatures.md 完成订阅与配置。
小结
useDeleteRevisions 以极简的 API(一个 mutation 函数 + 一个 mutation 结果对象)解决了"记录删除后修订数据清理"这一真实痛点。掌握它的 Hook 参数、返回值与 call-time 覆盖规则,配合 dataProvider 的三个修订方法与 onSettled 回调的时序控制,你就可以在自己的 react-admin 企业版应用中安全、完整地实现"删除记录并清除其全部历史修订"的闭环,让修订数据生命周期管理既可靠又可维护。