ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Refine v5 useDrawerForm 完全指南:在 Drawer 抽屉中实现创建、编辑与自动保存

Refine v5 useDrawerForm 完全指南:在 Drawer 抽屉中实现创建、编辑与自动保存 Refine v5 useDrawerForm 完全指南在 Drawer 抽屉中实现创建、编辑与自动保存【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine本文系统讲解 Refine v5 中useDrawerFormHook 的完整用法它如何把 Ant Design 的Form与Drawer组合起来在列表页中实现点击按钮弹出抽屉 → 填写表单 → 提交并关闭的经典 CRUD 交互。读完本文你将掌握 create / edit / clone 三种模式下的抽屉表单写法、syncWithLocation与 URL 同步、autoSave自动保存以及提交前后数据改写等实战技巧并能从 源码 层面理解其内部工作原理。useDrawerForm 是什么useDrawerForm是refinedev/antd包提供的 Hook用于在 Ant Design 的Drawer抽屉内管理一个表单。它直接返回 Ant DesignForm与Drawer两个组件所需的 props你只需要把它们展开到对应组件上即可const { formProps, drawerProps, show, saveButtonProps } useDrawerForm(); return ( Drawer {...drawerProps} Create saveButtonProps{saveButtonProps} Form {...formProps} layoutvertical {/* 表单字段 */} /Form /Create /Drawer / );关键的一点是useDrawerForm是从useForm扩展而来的。refinedev/antd中的useForm其文档见 use-form/index.md提供的全部能力——如数据获取、mutation、warnWhenUnsavedChanges、mutationMode、overtimeOptions等——在useDrawerForm中都能直接使用。这一点在源码的导出结构中也得到了印证useDrawerForm内部直接调用了useForm并把返回值透传出去见 useDrawerForm.ts。从源码类型定义看useDrawerForm支持四种actionshow | edit | create | clone见 useDrawerForm.ts其中show动作由useShow承担表单场景最常用的是create与edit。快速上手在抽屉中创建记录以最典型的文章列表 新建文章抽屉为例。页面主体用useTable渲染列表useDrawerForm管理抽屉内的创建表单import { HttpError } from refinedev/core; import React from react; import { Create, List, useDrawerForm, useTable } from refinedev/antd; import { Drawer, Form, Input, Select, Table } from antd; interface IPost { id: number; title: string; status: published | draft | rejected; } const PostList: React.FC () { const { tableProps } useTableIPost, HttpError(); // highlight-start const { formProps, drawerProps, show, saveButtonProps } useDrawerForm IPost, HttpError, IPost ({ action: create, }); // highlight-end return ( List canCreate // highlight-start createButtonProps{{ onClick: () { show(); }, }} // highlight-end Table {...tableProps} rowKeyid Table.Column dataIndexid titleID / Table.Column dataIndextitle titleTitle / /Table /List {/* highlight-start */} Drawer {...drawerProps} Create saveButtonProps{saveButtonProps} Form {...formProps} layoutvertical Form.Item labelTitle nametitle rules{[ { required: true, }, ]} Input / /Form.Item Form.Item labelStatus namestatus rules{[ { required: true, }, ]} Select options{[ { label: Published, value: published }, { label: Draft, value: draft }, { label: Rejected, value: rejected }, ]} / /Form.Item /Form /Create /Drawer {/* highlight-end */} / ); };这段代码的要点show()打开抽屉。在create模式下不需要传id因为创建时表单初始是空的。Create saveButtonProps{saveButtonProps}把保存按钮与表单提交绑定起来点击按钮会触发form.submit()随后执行提交逻辑成功后抽屉自动关闭、表单自动重置。Form {...formProps}负责管理表单状态、初始值、校验规则以及提交动作onFinish。编辑已有记录show(id) 与手动 EditButton编辑场景与创建几乎一致差别在于打开抽屉时需要把记录id传进去让表单拉取对应数据并回填同时用Edit组件包裹表单以支持删除等操作import { HttpError } from refinedev/core; import React from react; import { Edit, EditButton, List, useDrawerForm, useTable, } from refinedev/antd; import { Drawer, Form, Input, Select, Space, Table } from antd; interface IPost { id: number; title: string; status: published | draft | rejected; } const PostList: React.FC () { const { tableProps } useTableIPost, HttpError(); // highlight-start const { formProps, drawerProps, show, saveButtonProps, id } useDrawerForm IPost, HttpError, IPost ({ action: edit, warnWhenUnsavedChanges: true, }); // highlight-end return ( List canCreate createButtonProps{{ onClick: () show() }} Table {...tableProps} rowKeyid Table.Column dataIndexid titleID / Table.Column dataIndextitle titleTitle / Table.ColumnIPost titleActions dataIndexactions keyactions render{(_, record) ( // highlight-start Space EditButton hideText sizesmall recordItemId{record.id} onClick{() show(record.id)} / /Space // highlight-end )} / /Table /List {/* highlight-start */} Drawer {...drawerProps} Edit saveButtonProps{saveButtonProps} recordItemId{id} Form {...formProps} layoutvertical Form.Item labelTitle nametitle rules{[{ required: true }]} Input / /Form.Item Form.Item labelStatus namestatus rules{[{ required: true }]} Select options{[ { label: Published, value: published }, { label: Draft, value: draft }, { label: Rejected, value: rejected }, ]} / /Form.Item /Form /Edit /Drawer {/* highlight-end */} / ); };这里有一个容易忽略的细节Refine 不会自动为列表中的每条记录添加EditButton/需要手动把它放进操作列。手动放置的目的是让编辑按钮能拿到该行记录的id并通过show(record.id)通知useDrawerForm拉取数据Table.ColumnIPost titleActions dataIndexactions keyactions render{(_value, record) EditButton onClick{() show(record.id)} /} /务必把记录id传给show——对于edit和clone两种模式没有id就无法获取待编辑数据抽屉不会正常打开。这一行为在源码中有直接体现handleShow中当action为edit或clone时只有传入了showId或已存在id才会真正show()见 useDrawerForm.ts测试用例也验证了edit 模式不带 id 调用show()时抽屉保持关闭见 index.spec.tsx。此外Edit recordItemId{id}中的id来自useDrawerForm的返回值它记录了当前正在编辑的记录也是Edit头部删除按钮deleteButtonProps执行删除操作时的依据。源码视角useDrawerForm 内部做了什么理解返回值之前先看它的实现骨架useDrawerForm.ts这能帮你搞清楚哪些行为是开箱即用的抽屉可见性状态useDrawerForm内部使用useDrawer其实现见 hooks/drawer/useDrawer/index.tsx管理open状态初始值由defaultVisible决定默认false。表单逻辑全部委托给useForm数据获取、提交、mutation、autoSave等均来自useForm的返回值useDrawerForm只在其外层做抽屉相关的增强。提交后的收尾动作useDrawerForm包装了formProps.onFinish——先await onFinish(values)完成真正的提交然后根据autoSubmitClose默认true关闭抽屉再根据autoResetForm默认true调用form.resetFields()清空表单。drawerProps的固定默认值width: 500px、onClose指向内部handleClose、open为当前可见状态、forceRender: true立即渲染抽屉而非懒渲染。这些默认行为在 index.spec.tsx 的测试中都有覆盖例如autoSubmitClose为true时提交后抽屉关闭、autoSubmitClose为false时提交后抽屉保持打开、autoResetForm为true时提交后表单字段被清空见 index.spec.tsx。Properties配置项详解useDrawerForm的 props 完全继承自useForm完整列表见 use-form/index.md#properties因此resource、id、redirect、mutationMode、successNotification、errorNotification、meta、queryOptions、warnWhenUnsavedChanges、submitOnEnter、liveMode等全部可用。下面重点展开抽屉表单特有或最常用的几项。syncWithLocation抽屉状态与 URL 同步syncWithLocation默认为false设为true后抽屉的可见状态和当前记录的id会同步到 URL 查询参数中。这样刷新页面、前进后退时抽屉状态都能保留也便于分享带状态的链接。除了布尔值它还可以传对象{ key: string; syncId?: boolean }自定义 URL 查询参数的 keyconst drawerForm useDrawerForm({ syncWithLocation: { key: my-modal, syncId: true }, });key自定义查询参数名syncId为true时才把id同步进 URL。如果不自定义key源码会按drawer-{resource}-{action}的规则自动生成例如资源posts、动作edit时 key 为drawer-posts-edit见 useDrawerForm.ts。同步逻辑通过useGo在路由 query 中写入{ open: true, id }关闭时移除该参数见 useDrawerForm.ts。对应的测试会验证开启syncWithLocation后getOne请求的meta中会带上drawer-posts-edit: undefined见 index.spec.tsx。overtimeOptions请求超时提示当请求耗时过长、希望展示加载提示时传入overtimeOptions。interval是轮询间隔毫秒onInterval是每个间隔触发的回调。Hook 返回的overtime对象中elapsedTime表示已经过去的毫秒数请求完成时变为undefinedconst { overtime } useDrawerForm({ //... overtimeOptions: { interval: 1000, onInterval(elapsedInterval) { console.log(elapsedInterval); }, }, }); console.log(overtime.elapsedTime); // undefined, 1000, 2000, 3000 4000, ... // 用法示例超过 4 秒展示提示 { elapsedTime 4000 divthis takes a bit longer than expected/div; }autoSave自动保存开启后用户编辑表单并停止输入一段时间表单会自动提交保存无需手动点击保存按钮。⚠️autoSave只在edit模式生效。编辑已有数据时改动会自动保存创建新数据时仍需要手动保存。enabled默认false设为true开启useDrawerForm({ action: edit, autoSave: { enabled: true, }, });debounce自动保存的防抖时间默认1000毫秒useDrawerForm({ autoSave: { enabled: true, // highlight-next-line debounce: 2000, }, });onFinish在数据发送到服务器前做改写useDrawerForm({ autoSave: { enabled: true, // highlight-start onFinish: (values) { return { foo: bar, ...values, }; }, // highlight-end }, });invalidateOnUnmountHook 卸载时失效当前资源关联的list、many、detail查询可用invalidates属性选择要失效的查询类型默认falseuseDrawerForm({ autoSave: { enabled: true, // highlight-next-line invalidateOnUnmount: true, }, });invalidateOnClose抽屉关闭时失效查询语义与invalidateOnUnmount相同默认falseuseDrawerForm({ autoSave: { enabled: true, // highlight-next-line invalidateOnClose: true, }, });从源码看invalidateOnClose的失效逻辑实现在handleClose中仅当autoSaveProps.status success且配置了invalidateOnClose时才会调用invalidate({ invalidates: invalidates || [list, many, detail], ... })见 useDrawerForm.ts。autoSave还支持onMutationSuccess与onMutationError回调可通过参数isAutoSave判断本次 mutation 是否由自动保存触发相关行为继承自useForm详见 use-form/index.md#autosave。defaultFormValues表单默认值用于预填充表单初始数据useDrawerForm({ defaultFormValues: { title: Hello World, }, });也支持传入异步函数从服务端获取默认值加载期间可通过返回的defaultFormValuesLoading跟踪状态const { defaultFormValuesLoading } useDrawerForm({ defaultFormValues: async () { const response await fetch(https://my-api.com/posts/1); const data await response.json(); return data; }, }); 当action为edit或clone时异步defaultFormValues可能与记录数据加载产生竞态此时表单值以最后完成的操作为准。抽屉专用配置源码默认值除了继承自useForm的 propsuseDrawerForm还有三个抽屉相关配置均定义在 useDrawerForm.ts配置项默认值说明defaultVisiblefalse抽屉初始是否可见autoSubmitClosetrue提交成功后自动关闭抽屉autoResetFormtrue提交成功后自动重置表单字段Return Values返回值详解useDrawerForm返回useForm的全部返回值见 use-form/index.md#return-values外加一组抽屉专用的返回值。show打开Drawer的函数接受可选id参数。传入id时会拉取记录数据并回填到Formshow(); // 创建模式直接打开空表单 show(record.id); // 编辑/克隆模式携带 id 打开close关闭Drawer的函数等价于drawerProps.onClose。注意当warnWhenUnsavedChanges为true时close内部会先弹出未保存更改确认框源码中通过window.confirm实现见 useDrawerForm.ts确认后才真正关闭并清空id。saveButtonProps提交按钮所需的 propsdisabled、loading、onClick等。点击时触发form.submit()。源码中的实现为{ disabled: formLoading, onClick: () form.submit(), loading: formLoading }见 useDrawerForm.ts。可以直接传给Create、Edit的saveButtonProps也可以手动传给自定义按钮。deleteButtonProps删除按钮所需的 propsresource、recordItemId、onSuccess等。其onSuccess触发时会把id置为undefined并关闭抽屉见 useDrawerForm.ts。可手动传给自定义删除按钮。formProps管理Form状态与动作所必需的 props底层来自useForm。包含onValuesChange、initialValues、onFieldsChange、onFinish等 Ant Design Form 所需属性详见 use-form/index.md#formprops。:::note 注意onFinish与formProps.onFinish的区别直接从useDrawerForm返回的onFinish与useForm的onFinish相同而formProps.onFinish在其基础上做了增强——提交成功后自动关闭抽屉、清空字段。因此当你想在提交前改写数据时推荐使用formProps.onFinish并调用它让它继续接管提交后的收尾操作。:::drawerProps管理Drawer状态与动作的 props包含以下关键项属性默认值说明width500px抽屉宽度onClose内置handleClose关闭抽屉warnWhenUnsavedChanges为true时先弹出确认框。若自行覆盖该函数需要手动处理确认框逻辑openfalse抽屉当前可见状态forceRendertrue强制渲染抽屉而非懒渲染overtime{ elapsedTime?: number }请求超时时间统计请求完成后elapsedTime变为undefinedconst { overtime } useDrawerForm(); console.log(overtime.elapsedTime); // undefined, 1000, 2000, 3000 4000, ...autoSaveProps开启autoSave后返回包含 mutation 的data、error、status属性status取值为loading | error | idle | success。defaultFormValuesLoading当defaultFormValues是异步函数时在函数 resolve 前该值为true。完整返回值速查表Key说明类型show打开抽屉(id?: BaseKey) voidformAnt Design 表单实例FormInstanceTVariablesformProps表单 propsFormPropsdrawerProps抽屉 propsDrawerPropssaveButtonProps提交按钮 props{ disabled: boolean; onClick: () void; loading: boolean; }deleteButtonProps删除按钮 props{ resource?: string; recordItemId?: BaseKey; onSuccess?: (data: TData) void; mutationMode?: MutationMode; hideText?: boolean; }submit提交方法() voidopen抽屉是否打开booleanclose关闭抽屉() voidovertime超时加载状态{ elapsedTime?: number }autoSaveProps自动保存状态{ data?: UpdateResponseTData; error: HttpError \| null; status: loading \| error \| idle \| success }defaultFormValuesLoading默认值加载状态booleanFAQ提交前如何改写表单数据有时需要在表单数据发给 API 之前做转换。例如用户分别输入name和surname两个字段但 API 期望收到fullName。做法是覆盖Form的onFinish在其中调用formProps.onFinish传入转换后的数据import { Create, Drawer, useDrawerForm } from refinedev/antd; import { Form, Input } from antd; import React from react; export const UserCreate: React.FC () { // highlight-start const { formProps, drawerProps, saveButtonProps } useDrawerForm({ action: create, }); // highlight-end // highlight-start const handleOnFinish (values) { formProps.onFinish?.({ fullName: ${values.name} ${values.surname}, }); }; // highlight-end return ( Drawer {...drawerProps} Create saveButtonProps{saveButtonProps} // highlight-next-line Form {...formProps} onFinish{handleOnFinish} layoutvertical Form.Item labelName namename Input / /Form.Item Form.Item labelSurname namesurname Input / /Form.Item /Form /Create /Drawer ); };这样既完成了数据转换又保留了提交成功后自动关闭抽屉、重置表单的内置行为。类型参数Type ParametersuseDrawerForm是泛型 Hook与useForm的类型参数一致参数说明类型默认值TQueryFnData查询函数返回的数据继承BaseRecordBaseRecordBaseRecordTError自定义错误对象继承HttpErrorHttpErrorHttpErrorTVariables表单参数值的类型{}—TDataselect函数返回的数据继承BaseRecord未指定时使用TQueryFnDataBaseRecordTQueryFnDataTResponsemutation 函数返回的数据继承BaseRecord未指定时使用TDataBaseRecordTDataTResponseError自定义错误对象继承HttpError未指定时使用TErrorHttpErrorTError在官方示例中查看完整实现仓库的 examples/form-antd-use-drawer-form 提供了可直接运行的完整示例其中 list.tsx 同时演示了一个页面中并存Create 抽屉与Edit 抽屉两个useDrawerForm实例都开启了syncWithLocation: true各自独立与 URL 同步编辑抽屉配合Edit recordItemId{id} deleteButtonProps{deleteButtonProps}实现编辑 删除第三个Show 抽屉使用useShow展示记录详情用于对比表单抽屉与只读展示抽屉两种模式。小结useDrawerForm的核心价值在于把表单逻辑 抽屉开关 提交后收尾三件事封装在一个 Hook 里表单能力完全复用useForm抽屉状态由内部useDrawer管理提交成功后自动关闭并重置。结合syncWithLocation的状态持久化、autoSave的自动保存与overtimeOptions的加载反馈你可以在列表页中以极少的样板代码实现完整、顺滑的抽屉式 CRUD 交互。对其内部实现感兴趣的读者可以继续研读 useDrawerForm.ts 与配套测试 index.spec.tsx验证本文涉及的每一个默认行为。【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表