- 前端
- UI组件
【免费下载链接】react-admin
A frontend Framework for single-page applications on top of REST/GraphQL APIs, using TypeScript, React and Material Design
是 react-admin 生态中用于防止表单数据丢失的企业级组件:它把用户在 create/edit 表单中已填写的数据自动保存到 ra-core Store 中,用户因导航离开页面后再次返回时,组件会把暂存数据重新应用到表单,避免误触链接导致输入内容全部丢失。本文基于 docs_headless/src/content/docs/AutoPersistInStoreBase.md 完整展开该组件的设计目标、使用方法、全部 Props(getStoreKey、maxAge、notification)以及它与 ra-core Store 的底层协作机制,读完即可在你的 react-admin 应用中直接落地一套表单草稿自动恢复方案。
一、为什么需要表单数据自动保存
在后台管理应用中,用户常常在编辑或新建一条记录时填写了大量内容,却因为误点导航、刷新页面或切换到其他页面而丢失全部输入。传统的防丢方案需要开发者自己写localStorage读写逻辑、自行管理存储键与清理时机,代码既分散又容易出错。
<AutoPersistInStoreBase>把这一整套机制封装成单一组件:
- 边改边存:表单数据一旦发生变化,立即写入 Store;
- 挂载重放:当用户再次回到该表单页面(组件重新挂载)时,自动把暂存数据重新应用到表单;
- 提交即清:用户成功提交表单后,暂存数据被移除,避免下次进入表单时出现过期草稿;
- 可主动放弃:用户在通知栏中点击 "Cancel" 按钮即可放弃预填充、将表单重置为默认值;
- 严格本地化:暂存数据不会发送到服务器,仅通过 ra-core 的 Store 持久化,并在用户登出时被清除。
适用前提:该组件属于 react-admin Enterprise Edition 源码说明其底层存储机制。
二、快速开始:在表单中接入组件
在 react-admin 表单内部(例如<EditBase>+<Form>组合)直接添加<AutoPersistInStoreBase>即可,无需任何额外配置:
import { AutoPersistInStoreBase, useAutoPersistInStoreContext } from '@react-admin/ra-core-ee'; import { EditBase, Form, Translate, useEvent, useCloseNotification } from 'ra-core'; import { Button, TextInput } from 'my-react-admin-ui-library'; const PostEdit = () => ( <EditBase> <Form> <TextInput source="title" /> <TextInput source="teaser" /> <AutoPersistInStoreBase notification={<AutoPersistNotification />} /> </Form> </EditBase> ); const AutoPersistNotification = () => { const closeNotification = useCloseNotification(); const { reset } = useAutoPersistInStoreContext(); const cancel = useEvent((event: React.MouseEvent) => { event.preventDefault(); reset(); closeNotification(); }); return ( <div> <Translate i18nKey="ra-form-layout.auto_persist_in_store.applied_changes" /> <Button label="ra.action.cancel" onClick={cancel} /> </div> ); };组件会自动完成"on change 时保存表单数据、表单再次挂载时重新应用"的全部工作,并且同时适用于 create 和 edit 两种表单。在 create 表单中它保存的是用户尚未提交的新建数据,在 edit 表单中它保存的是对既有记录的未提交修改。
notification是必填 Prop,用于在组件把暂存数据重新应用到表单时,向用户展示一条可操作的提示(详见下文notification小节)。
三、Props 总览
| Prop | Required | Type | Default | Description |
|---|---|---|---|---|
notification | Required | ReactNode | - | 一条通知元素,在暂存数据被重新应用时展示给用户 |
getStoreKey | - | function | - | 自定义存储键的函数,用于覆盖默认的 Store key |
maxAge | - | number | - | 存储值的过期秒数,超过该时长的旧值会在写入新值时被自动清理 |
其中getStoreKey与maxAge互斥:提供getStoreKey后,maxAge特性会被禁用(详见下文)。
四、getStoreKey:自定义存储键
默认存储键格式
<AutoPersistInStoreBase>通过 ra-core 的 useStoreContext 保存当前表单数据,默认使用的 Store key 为:
ra-persist-[RESOURCE_NAME]-[RECORD_ID]例如编辑posts资源中 ID 为123的记录时,Store key 为ra-persist-posts-123;而在 create 表单中,记录 ID 部分会被替换为"create",即ra-persist-posts-create。这套命名规则保证了不同资源、不同记录之间的暂存数据互不干扰。
自定义 key 函数
如果你希望调整 key 的命名(例如统一前缀、纳入更多上下文信息),可以传入getStoreKey函数。它接收两个参数:
resource:当前资源名;record:当前记录对象(仅在 useEditContext 等编辑上下文中存在;create 表单中通常为undefined)。
<AutoPersistInStoreBase getStoreKey={ (resource: ResourceContextValue, record: RaRecord<Identifier> | undefined) => `my-custom-persist-key-${resource}-${record && record.hasOwnProperty('id') ? record.id : 'create'}` } notification={<AutoPersistNotification />} />示例中通过record.hasOwnProperty('id')判断当前处于编辑还是新建状态,并据此构造 key 后缀——这与默认行为中"create 表单用create代替记录 ID"的语义保持一致,说明该函数完全接管了 key 的生成逻辑,你可以在其中自由拼接资源名、记录 ID 甚至多租户标识。
五、maxAge:自动清理过期暂存数据
为什么需要过期机制
Store(尤其是基于localStorage的 localStorageStore)容量有限,浏览器通常为localStorage提供约 5MB 的配额。如果每个资源、每条记录都长期保留一份暂存数据,大量 key 会持续占用存储空间。为此,maxAge允许你指定一个以秒为单位的有效期:每当写入新值时,超过该时长的旧暂存数据会被自动从 Store 中移除。
<AutoPersistInStoreBase maxAge={10 * 60} // 10 分钟 notification={<AutoPersistNotification />} />底层依赖:Store 的listItems能力
maxAge的实现依赖 Store 提供的listItems方法(用于枚举并清理过期项)。这一能力在 ra-core 的 Store 接口中被定义为可选方法:
- 接口定义见 packages/ra-core/src/store/types.ts:
listItems?: (keyPrefix?: string) => Record<string, unknown>; - 默认的两个 Store 实现均具备该方法:
localStorageStore在 packages/ra-core/src/store/localStorageStore.ts 中实现,遍历浏览器存储中所有以RaStore前缀开头的 key(排除内部version键),解析 JSON 后按前缀返回;memoryStore在 packages/ra-core/src/store/memoryStore.tsx 中实现,直接遍历内存 Map 中匹配前缀的条目。
对应行为也有测试覆盖:listItems会返回所有带指定前缀的条目、不带前缀时返回全部条目,见 packages/ra-core/src/store/localStorageStore.spec.ts 与 packages/ra-core/src/store/memoryStore.spec.tsx。
因此使用maxAge时有两条重要约束:
- Store 必须实现
listItems:ra-core 内置的localStorageStore与memoryStore都满足要求;如果接入自定义 Store 实现,需要自行确认其是否提供listItems; - 与
getStoreKey互斥:一旦传入getStoreKey自定义了 key 前缀,maxAge过期清理即被禁用——原因是组件无法再依据ra-persist-前缀安全地枚举与清理你自定义命名的 key。
六、notification:让用户决定是否保留暂存数据
当<AutoPersistInStoreBase>把 Store 中的暂存数据重新应用到表单时,react-admin 会通过 useNotify 的notify函数向用户展示一条通知。notification元素正是被传递给notify的那条通知内容。
默认的通知承载着两层信息:告知用户"之前未保存的修改已被恢复",并提供撤销入口,让用户点击 Cancel 放弃暂存数据、把表单重置为初始默认值。
import { AutoPersistInStoreBase, useAutoPersistInStoreContext } from '@react-admin/ra-core-ee'; import { EditBase, Form, Translate, useEvent, useCloseNotification } from 'ra-core'; import { Button, TextInput } from 'my-react-admin-ui-library'; const PostEdit = () => ( <EditBase> <Form> <TextInput source="title" /> <TextInput source="teaser" /> <AutoPersistInStoreBase notification={<AutoPersistNotification />} /> </Form> </EditBase> ); const AutoPersistNotification = () => { const closeNotification = useCloseNotification(); const { reset } = useAutoPersistInStoreContext(); // 允许用户放弃暂存的修改,并将表单重置为其默认值 const cancel = useEvent((event: React.MouseEvent) => { event.preventDefault(); reset(); closeNotification(); }); return ( <div> <Translate i18nKey="ra-form-layout.auto_persist_in_store.applied_changes" /> <Button label="ra.action.cancel" onClick={cancel} /> </div> ); };该自定义通知的关键点:
useAutoPersistInStoreContext:从组件内部上下文中取出reset方法,用于清除 Store 中已应用的暂存数据并将表单重置为默认值;useCloseNotification:关闭当前通知,与reset配合完成"放弃草稿"的完整交互;useEvent:保证事件处理函数引用稳定,且始终访问最新的闭包状态;Translate+ i18n key:通知文案通过 i18n keyra-form-layout.auto_persist_in_store.applied_changes解析,天然支持多语言。
七、自定义通知文案:notificationMessage
除了自定义整个通知元素,你还可以只替换通知文字。在<AutoPersistInStoreBase>的配套文档 docs/AutoPersistInStore.md 中给出了默认行为与覆盖方式:
- 默认通知文案的 i18n key 为
ra-form-layout.auto_persist_in_store.applied_changes,默认英文翻译是"Applied previous unsaved changes"; - 通过
notificationMessage属性可以直接指定文案,也可以传入一个翻译 key:
<AutoPersistInStoreBase notificationMessage="Modifications applied" /><AutoPersistInStoreBase notificationMessage="myroot.message.auto_persist_applied" />说明:
notificationMessage在 docs_headless/src/content/docs/AutoPersistInStoreBase.md 的 Props 表格中未列出,但在完整版文档 docs/AutoPersistInStore.md 中有明确说明,可视为组件在完整形态下提供的文案定制入口。消息文案经由 i18n Provider 翻译,因此默认情况下多语言应用无需额外配置即可工作。
八、数据生命周期与 Store 集成原理
暂存数据去哪儿了
<AutoPersistInStoreBase>的暂存数据不会离开浏览器。它通过 ra-core 的全局 Store 持久化,而 ra-core 的 Store 默认基于浏览器localStorage实现(localStorage不可用时自动降级为内存存储),详见 docs_headless/src/content/docs/Store.md 中对 Store 的定位:"a global, synchronous, persistent store",并明确"store is emptied when the user logs out"。
由此可以梳理出暂存数据的完整生命周期:
| 时机 | 行为 |
|---|---|
| 表单字段发生变化 | 表单数据写入 Store(key 为ra-persist-[资源名]-[记录ID]) |
| 用户离开页面后再次返回 | 组件重新挂载,从 Store 读取并重新应用到表单,同时弹出通知 |
| 用户点击通知中的 Cancel | 调用上下文reset清除暂存数据、重置表单,并关闭通知 |
| 用户成功提交表单 | 暂存数据被移除(避免残留过期草稿) |
| 用户登出 | Store 整体清空,暂存数据随之消失 |
源码级佐证:Store 的关键能力
<AutoPersistInStoreBase>所依赖的 Store 抽象在开源仓库中可以直接查看:
- 接口定义:packages/ra-core/src/store/types.ts 定义了
getItem/setItem/removeItem/removeItems/reset/subscribe/listItems等核心方法; - localStorage 实现:packages/ra-core/src/store/localStorageStore.ts 中
localStorageStore(version, appKey)返回一个带RaStore前缀的 Store,并在每次写入时通过JSON.stringify序列化;它还通过window.addEventListener('storage', ...)实现了跨标签页同步; - 内存实现:packages/ra-core/src/store/memoryStore.tsx 中
memoryStore(initialStorage)用 Map 保存键值对,并在setup前暂存待写入项; - Store 订阅模型:两个实现都通过
subscribe(key, callback)支持按 key 订阅变更,这也是 react-admin 中useStore等 Hook 实时响应存储变化的底层机制。
九、注意事项与使用建议
- 企业版限制:
<AutoPersistInStoreBase>及配套的useAutoPersistInStoreContext来自@react-admin/ra-core-ee,需要有效的 Enterprise Edition 订阅;开源版 react-admin 仓库中不包含其实现,本文描述的 API 以其官方文档为准。 - 明确不回传服务器:暂存数据只存在浏览器 Store 中,与后端数据源(REST/GraphQL)完全隔离,不会污染服务端数据。
maxAge与getStoreKey二选一:自定义 key 后过期清理自动失效,如需同时获得自定义命名与过期清理,需在自定义 Store 或清理策略层面自行处理。- Store 容量规划:若长期大量使用自动保存,建议合理设置
maxAge(例如 10 分钟),避免localStorage被历史草稿占满。 - 与表单上下文配合:组件必须放置在 react-admin 表单上下文内部(如
<Form>、<SimpleForm>、<TabbedForm>等),它依赖上下文获取资源名、记录与表单值。开源版中的表单组件详见 docs_headless/src/content/docs/Form.md 与 docs_headless/src/content/docs/SimpleForm.md。
十、进一步探索
- 组件文档:docs_headless/src/content/docs/AutoPersistInStoreBase.md、完整版 docs/AutoPersistInStore.md,官方演示视频位于 docs/img/AutoPersistInStore.mp4;
- Hook 版本:docs_headless/src/content/docs/useAutoPersistInStore.md 提供了
useAutoPersistInStore命令式用法,适合封装自定义表单布局; - Store 文档:docs_headless/src/content/docs/Store.md、useStoreContext;
- 通知与上下文 API:useNotify、useEditContext;
- Store 源码:接口定义 packages/ra-core/src/store/types.ts、实现 packages/ra-core/src/store/localStorageStore.ts、packages/ra-core/src/store/memoryStore.tsx 及其测试 localStorageStore.spec.ts、memoryStore.spec.tsx。
- 前端
- UI组件
【免费下载链接】react-admin
A frontend Framework for single-page applications on top of REST/GraphQL APIs, using TypeScript, React and Material Design
相关推荐
Cilium Security Identities 详解:基于标签的安全身份分配、取值范围与作用域
Cilium Security Identities 详解:基于标签的安全身份分配、取值范围与作用域 导读 Security Identities(安全身份)是
云原生网络服务网格可观测性网络安全eBPFreact-jsonschema-form表单自动保存功能实现:提升用户体验
react jsonschema form表单自动保存功能实现:提升用户体验 你是否曾遇到过这样的情况:填写了很长的表单,不小心刷新页面或关闭浏览器后,所有内容
前端UI组件react-admin 的 `<AutoPersistInStore>`:基于 Store 的表单数据自动持久化与防丢失方案
react admin 的 <AutoPersistInStore :基于 Store 的表单数据自动持久化与防丢失方案 <AutoPersistInStore
前端UI组件
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考