Form 表单
这个页面不再只给你一个“能跑”的表单,而是把表单最关键的使用思路补进 Rue 当前稳定实现:基础提交、命令式实例、跨字段依赖、条件区域、动态列表、搜索表单,以及它们背后的使用约束。
何时使用
- 需要把多个输入控件组织成一个稳定的提交单元,并统一收集 values、errors 和 touched 状态。
- 需要显式拿到 form 实例,在按钮、摘要面板、列表操作和业务动作之间共享同一份状态。
- 需要处理动态数组字段,例如成员名单、规则条目、白名单或批量配置项。
默认值放在 Form 上
字段被 Form.Item 接管后会进入受控模式。需要初始值时,优先放到 initialValues,而不是给 Input 传 defaultValue。
- 单字段初始化优先级低于 Form initialValues
- 动态列表默认值放到 Form 或 Form.List
- resetFields 会回到 initialValues 快照
Checkbox 不是 value
Checkbox、Toggle 这类组件的值语义不是 value,而是 checked。需要通过 valuePropName 切换绑定属性。
- Checkbox / Switch 类组件使用 checked
- 否则会出现“值变了但控件没联动”的错觉
dependencies 和 shouldUpdate 分工不同
dependencies 更适合“某个字段依赖另一个字段重新校验”,shouldUpdate 更适合“根据整张表的值决定是否渲染一个区域”。
- 确认密码用 dependencies
- 条件区域和 JSON 预览用 shouldUpdate
- 不要在同一职责上混用两者
显式传 form 更稳定
Rue 当前 runtime 下,推荐始终显式持有并传递 form 实例。页面级按钮、摘要卡片、提交动作也都围绕这一个实例展开。
- 优先 Form.useForm()
- render(form) 里把 form 可以传给 Item / List
- 不要依赖隐式祖先解析
Form.useForm()或 render={form => <Form.Item form={form} name="title" />}显式持有并传递 form。 下面所有示例 都按这个稳定路径组织,不再依赖隐式祖先解析;你也会在示例里反复看到几条关键规则: `initialValues` 优先于子字段默认值,`Checkbox` 要切到 `checked` 语义,`dependencies` 和 `shouldUpdate` 分工不同。# Basic submit and validation
基础提交、规则校验与 Checkbox 的 checked 绑定。
这个示例覆盖基础提交路径:基础收集、规则校验、Checkbox 的 checked 绑定,以及失败时滚到错误字段。
{
"profile": {
"name": "Rue Design",
"email": "team@rue.dev"
},
"agree": true
}等待提交,右侧会展示 payload 或校验摘要。
# Form instance and reactive summary
用 Form.useForm 和 Form.useWatch 把同一个表单实例共享给按钮和摘要面板。
这里把表单实例方法和 Watch Hooks 两类示例:同一个实例被按钮、摘要卡片和提交流程共享,字段变化会同步反映到右侧。
等待实例方法操作。
# Validation timing and dependencies
把 validateTrigger、dependencies 和 warningOnly 放进一个更贴近注册/发布配置场景的表单里。
这组示例把最常用的跨字段模式放到一起:`validateTrigger` 控制时机,`dependencies` 负责联动校验,`warningOnly` 用于规范提醒。
试试先输入密码,再修改确认密码,观察依赖字段的重新校验。
# Normalize and value mapping
演示 getValueProps、normalize 和 messageVariables 在实际业务表单里的配合方式。
这个示例演示 `getValueProps + normalize` 思路:显示层和存储层可以不同,但写入 Form store 前必须同步归一化。
{
"issueId": "2048",
"slug": "release-planning",
"branch": "main"
}# Conditional fields with shouldUpdate
用 shouldUpdate 按需挂载额外区域,而不是让每个字段都承担条件渲染职责。
这个示例演示 `shouldUpdate` 使用方式:字段本身负责绑定,条件区域负责根据全表单值决定是否渲染额外内容。
approveBy: 未挂载
开启灰度发布后,额外字段会由 shouldUpdate 动态挂载。
# Composite controls with noStyle
一个视觉行里组合多个字段时,用外层 Item 负责排版,内部 noStyle Item 只做字段绑定。
这个示例演示复合表单控件的常见组织方式 思路:一个视觉行可以包含多个真实字段,但绑定必须落在内部 noStyle Item 上。
noStyle 更适合把字段绑定嵌进自定义布局,而不是单独承担一整行表单结构。
# Dynamic list
用 Form.List 组织动态数组字段,并补上列表级规则、重排操作和 ErrorList。
rules 挂在 Form.List 上,校验整个 members 数组。
operation.move 保持字段状态跟随成员顺序移动。
Form.ErrorList 只展示列表自身的错误,不和单个字段提示混在一起。
# Long form scrolling
在可滚动长表单里同时展示 scrollToField 和 scrollToFirstError 的定位行为。
这个示例把手动 scrollToField 和提交失败自动滚动放进同一个长表单容器里,方便直接比较两种行为。
点击左侧按钮可调用 scrollToField;提交时会用 scrollToFirstError 自动滚到首个错误字段。
# Advanced search
把高级搜索表单整理成 Rue 实现:网格字段、独立按钮区、统一实例驱动。
这个示例更接近常见的高级搜索表单:字段采用网格布局,按钮区保持独立,search 和 reset 仍由同一实例驱动。
暂无 query string
尚未执行检索。
使用说明
下面这些说明不是泛泛而谈,而是把表单里最容易踩坑、同时又和 Rue 当前组件强相关的部分抽出来。先看这些规则,再回头看上面的示例,理解会快很多。
为什么推荐显式传 form?
因为 Rue 当前 runtime 还不支持稳定地自动解析最近 Form 实例。显式传递是当前最稳的写法。
为什么 Checkbox 不跟值同步?
大多数时候是忘了把 Form.Item 的 valuePropName 改成 checked。Form 默认只会往 value 上注入值。
为什么 defaultValue 不生效?
字段一旦被 Form.Item 接管,就会进入受控模式。应改用 Form 的 initialValues,或者通过 setFieldsValue 更新。
为什么 Form.List 里的 Item initialValue 不推荐?
动态列表的初始结构应放在 Form 或 Form.List 上,而不是子 Item 上。
什么时候用 getValueProps + normalize?
当显示值和存储值不一致时,例如 #2048、百分比、单位后缀、kebab-case slug,推荐一进一出都显式写出来。
scrollToFirstError 为什么可能失效?
如果你包装了自定义控件,需要确保 Form 注入的 id 最终落到真实 DOM 输入节点上,否则无法精确定位。
API
Form 当前推荐的心智模型仍然是显式实例驱动:页面或业务组件持有实例,Form 负责布局与校验,Item / List 通过同一实例绑定字段。下表只列出 Rue 当前最稳定、最值得依赖的那一层 API。
Form
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
form | 显式注入 Form 实例,在当前 Rue runtime 下这是最稳定的共享方式。 | FormInstance | - |
initialValues | 挂载时写入初始值;后续可通过实例方法重置回该快照。 | Record<string, any> | - |
layout | 设置整体布局,可选 horizontal、vertical、inline。 | horizontal | vertical | inline | horizontal |
component | 指定根节点标签,默认渲染为 form。 | string | false | form |
name | 设置 Form 名称,会参与字段 id 生成以及 scrollToField 定位。 | string | - |
validateMessages | 覆盖默认校验模板,适合做团队统一文案或本地化。 | FormValidateMessages | - |
validateTrigger | 全局校验触发时机,可由 Form.Item 单独覆盖。 | string | string[] | onChange |
scrollToFirstError | 提交失败后滚动到第一个错误字段,适合长表单。 | boolean | ScrollIntoViewOptions | false |
preserve | 字段卸载后是否保持值,常用于条件区域和分页表单。 | boolean | true |
render | 通过 render(form) 直接拿到实例并组织表单内容。 | (form: FormInstance) => any | - |
onValuesChange | 任意字段变化时触发,返回变更值和全量值。 | (changedValues, allValues) => void | - |
onFinish / onFinishFailed | 提交成功或失败时触发,便于保存 payload 或回显错误摘要。 | (values) => void / (info) => void | - |
Form.Item
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
name | 字段路径,支持字符串或数组路径。 | NamePath | - |
label | 字段标签,水平布局下默认追加冒号。 | any | - |
rules | 声明式校验规则,支持 required、type、pattern、自定义 validator。 | FormRule[] | - |
render | 最推荐的字段渲染方式,可拿到 controlProps、meta 与 form。 | (controlProps, meta, form) => any | - |
dependencies | 声明上游依赖字段,上游变化时会重新触发当前项校验。 | NamePath[] | - |
validateTrigger | 覆盖单字段校验时机,例如 onBlur。 | string | string[] | onChange |
valuePropName | 把字段值映射到 checked 等非 value 属性,适合 Checkbox。 | string | value |
getValueProps / normalize | 分别控制“store -> 控件”和“控件 -> store”的值转换。 | function / function | - |
extra / help | 额外说明和帮助信息;未显式传 help 时会展示校验消息。 | any | - |
messageVariables | 修改校验模板中的占位变量,适合自定义 label 文案。 | Record<string, string> | - |
hasFeedback | 显示校验状态反馈图标,通常与 Input 联用。 | boolean | false |
shouldUpdate | 把 Item 作为 render consumer 使用,按条件响应全表单变化。 | boolean | (prev, next) => boolean | - |
noStyle | 只保持字段绑定能力,不输出额外布局壳层。 | boolean | false |
Form.List and Hooks
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
Form.List name | 声明数组字段路径,驱动动态条目渲染。 | NamePath | - |
Form.List render | render(fields, operation, meta) 返回列表 UI。 | (fields, operation, meta) => any | - |
Form.List rules | 为整个列表声明规则,通常与 Form.ErrorList 一起使用。 | FormRule[] | - |
operation.add / remove / move | 列表操作对象,负责增删改顺序。 | FormListOperation | - |
Form.ErrorList | 渲染列表级错误与警告,适合最小数量、重复值等规则。 | FC<FormErrorListProps> | - |
Form.useForm() | 创建或复用显式实例,适合跨按钮、摘要卡片和表单主体共享。 | () => [FormInstance] | - |
Form.useWatch(name, form) | 订阅某个字段并在当前组件内响应式读取它。 | (name, form?) => any | - |
form.validateFields() | 在命令式流程中提前执行一次校验。 | () => Promise<any> | - |
form.getFieldsError() | 读取当前错误和警告快照,适合摘要面板。 | () => FieldError[] | - |
form.scrollToField(name) | 手动滚动到指定字段,适合长表单和分步表单。 | (name, options?) => void | - |
form.submit() | 从外部按钮或命令式操作触发一次提交流程。 | () => void | - |