Refine v5 Mantine 教程:SaveButton 组件——表单保存动作的完整实现与定制指南
【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards & B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine
<SaveButton>是 Refine v5 中专门用于表单保存动作的按钮组件,基于 Mantine<Button>构建,是 Edit / Create 页面表单提交流程的核心交互元素。本文以 documentation/docs/ui-integrations/mantine/components/buttons/save-button/index.md 为主体,深入讲解其用法、hideText属性,并结合仓库源码剖析其底层实现与测试验证。
SaveButton 是什么
<SaveButton>是@refinedev/mantine包导出的按钮组件,它在 Mantine<Button>返回的saveButtonProps会为按钮附加提交、校验、加载态等行为。
从源码实现看,SaveButton 组件 的核心逻辑非常简洁:
// packages/mantine/src/components/buttons/save/index.tsx export const SaveButton: React.FC<SaveButtonProps> = ({ hideText = false, svgIconProps, children, ...rest }) => { const { label } = useSaveButton(); const { variant, styles, ...commonProps } = rest; return hideText ? ( <ActionIcon ...> <IconDeviceFloppy size={18} {...svgIconProps} /> </ActionIcon> ) : ( <Button variant="filled" leftIcon={<IconDeviceFloppy size={18} {...svgIconProps} />} ...> {children ?? label} </Button> ); };几个值得注意的设计点:
- 默认渲染为 Mantine
<Button>,使用filled变体,左侧自带一个软盘图标(IconDeviceFloppy); - 按钮文字优先取
children,未传时回退到useSaveButton()返回的label; label并非硬编码字符串,而是通过useActionableButtonHook 从 i18n 翻译函数获取,key 为buttons.save,fallback 为humanize("save"),即 "Save",详见 actionable-button/index.tsx 与 hooks/button/index.tsx。
这意味着 SaveButton 天然支持多语言环境:只要在 i18n Provider 中配置buttons.save的翻译,按钮文字就会自动本地化。
在 Edit 页面中使用 SaveButton
SaveButton 最常见的用法是与useForm配合,出现在编辑页面的表单提交场景。原文档给出了完整示例,核心思路是把useForm返回的saveButtonProps透传给<Edit>组件的同名属性,由Edit内部的页脚自动渲染 SaveButton:
import { Edit, useForm, useSelect } from "@refinedev/mantine"; import { Select, TextInput } from "@mantine/core"; const PostEdit: React.FC = () => { const { saveButtonProps, getInputProps, refineCore: { query }, } = useForm<IPost>({ initialValues: { title: "", status: "", category: { id: "", }, }, validate: { title: (value) => (value.length < 2 ? "Too short title" : null), status: (value) => (value.length <= 0 ? "Status is required" : null), category: { id: (value) => (value.length <= 0 ? "Category is required" : null), }, }, }); const postData = query?.data?.data; const { selectProps } = useSelect<ICategory>({ resource: "categories", defaultValue: postData?.category.id, }); return ( <Edit saveButtonProps={saveButtonProps}> <form> <TextInput mt={8} label="Title" placeholder="Title" {...getInputProps("title")} /> <Select mt={8} label="Status" placeholder="Pick one" {...getInputProps("status")} data={[ { label: "Published", value: "published" }, { label: "Draft", value: "draft" }, { label: "Rejected", value: "rejected" }, ]} /> <Select mt={8} label="Category" placeholder="Pick one" {...getInputProps("category.id")} {...selectProps} /> </form> </Edit> ); }; interface ICategory { id: number; title: string; } interface IPost { id: number; title: string; status: "published" | "draft" | "rejected"; category: { id: number }; }saveButtonProps 里到底装了什么
saveButtonProps之所以能把"保存"动作接到表单上,是因为 Edit 组件 在内部对传入的 props 做了加工:
// packages/mantine/src/components/crud/edit/index.tsx const saveButtonProps: SaveButtonProps = { ...(isLoading ? { disabled: true } : {}), ...saveButtonPropsFromProps, };然后将其渲染在默认页脚按钮区域:
const defaultFooterButtons = ( <> {isDeleteButtonVisible && <DeleteButton {...deleteButtonProps} />} <SaveButton {...saveButtonProps} /> </> );也就是说,在isLoading为true(例如查询数据或提交中)时,保存按钮会自动进入disabled状态,避免用户重复提交。Create 页面 create/index.tsx 采用了完全相同的模式。这一点对表单交互体验至关重要,也是saveButtonProps相比手动编写<button type="submit">的核心价值所在。
属性详解:hideText
hideText用于控制按钮文字是否显示。当设为true时,按钮只保留图标(软盘),适合工具栏、紧凑布局或已附带文字说明的场景:
import { SaveButton } from "@refinedev/mantine"; const MySaveComponent = () => { return <SaveButton hideText />; };从源码可以看到hideText的完整渲染逻辑(save/index.tsx):
hideText为true时:渲染为 Mantine<ActionIcon>(图标按钮),默认variant="filled"、color="primary";如果调用方传了variant,则通过mapButtonVariantToActionIconVariant做一次映射(definitions/button/index.ts),该映射仅将white变体转换为default,其余原样透传;hideText为false(默认)时:渲染为常规<Button>,文字为children ?? label。
两种形态都会带上data-testid(RefineButtonTestIds.SaveButton)与className(RefineButtonClassNames.SaveButton),便于测试与全局样式定制。
API 一览
SaveButton的 Props 类型定义在 ui-types/src/types/button.tsx:
export type RefineSaveButtonProps< TComponentProps extends {} = Record<string, unknown>, TExtraProps extends {} = {}, > = RefineButtonCommonProps & RefineButtonLinkingProps & TComponentProps & TExtraProps & {};展开后,常用属性包括:
| 属性 | 类型 | 说明 |
|---|---|---|
hideText | boolean | 是否隐藏文字只显示图标,默认false |
children | ReactNode | 自定义按钮文字,未传时使用默认 label("Save") |
onClick | PointerEventHandler | 点击回调,来自RefineButtonLinkingProps |
variant | ButtonVariant | Mantine 按钮变体,透传给底层 Button |
svgIconProps | IconProps | 透传给软盘图标的属性(Mantine 包特有扩展) |
在 mantine 包的类型定义 中,SaveButtonProps还额外组合了svgIconProps这类 Mantine 特有的扩展属性,用于定制图标尺寸、颜色等。由于TComponentProps默认继承 Mantine<Button>的全部ButtonProps,size、color、radius、loading等原生属性均可直接透传。
用 Refine CLI swizzle 定制
原文档特别提到:可以用Refine CLI对组件执行 swizzle 操作,将其"弹出"到你的项目源码中,再进行深度定制。swizzle 后组件会成为项目内的普通组件,你可以直接修改其 JSX、样式或逻辑,而不受包更新影响。相关 CLI 说明见 packages/cli 的文档与实现。
源码验证:测试覆盖了什么
@refinedev/ui-tests提供了一套跨 UI 库通用的 SaveButton 测试,Mantine 实现通过一行代码接入(save/index.spec.tsx):
import { buttonSaveTests } from "@refinedev/ui-tests"; import { SaveButton } from "./"; describe("Save Button", () => { buttonSaveTests.bind(this)(SaveButton); });通用测试用例定义在 ui-tests/src/tests/buttons/save.tsx,覆盖了五个核心行为:
- 基本渲染:
<SaveButton />能正常挂载; - test-id:渲染结果中存在
RefineButtonTestIds.SaveButton; - children 渲染:传入
<SaveButton>refine</SaveButton>时,页面文本中出现 "refine"; - hideText:
<SaveButton hideText />时,页面文本中不再出现 "Save"(只显示图标); - 点击回调:点击按钮触发
onClick且只触发一次。
同时,useActionableButton本身也有单测(actionable-button/index.spec.tsx),验证了save/export/import三种类型分别返回 "Save" / "Export" / "Import" 的默认标签。这些测试保证了 SaveButton 在四个主流 UI 库(Ant Design、MUI、Mantine、Chakra UI)之间的行为一致性。
小结
在 Refine v5 中,SaveButton 是一个"薄封装 + 深度集成"的组件:
- 呈现层由 Mantine
<Button>承载,支持hideText纯图标模式与svgIconProps图标定制; - 行为层由
useForm的saveButtonProps注入,通过 Edit / Create 页脚自动获得提交、校验、加载态禁用等能力; - 文案层由
useActionableButton提供,默认 "Save",可经由buttons.save翻译 key 实现 i18n 本地化; - 可定制性由 Refine CLI swizzle 保障,弹出源码后可按需改造。
掌握 SaveButton 的用法与内部机制,是构建专业、可维护的 Mantine 后台表单界面的基础一步。
【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards & B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考