antd Form 表单组件完全指南:数据域管理、校验与动态表单实战(基于 ant-design 组件库)
本指南以 ant-design 仓库中 Form 官方中文文档 为骨架,结合 组件源码、表单实例实现 与 示例代码 展开。你将系统掌握 Form 的「何时使用」场景、Form / Form.Item / Form.List / Form.Provider 四大 API 的完整配置、四个 Hooks(useForm、useFormInstance、useWatch、Form.Item.useStatus)的用法,以及 dependencies、shouldUpdate、normalize、动态增删字段、滚动到错误等高频实战问题的原理与解法。
何时使用
Form 是 antd 的高性能表单控件,自带数据域管理,涵盖数据录入、校验以及对应样式。典型使用场景有两个:
- 用于创建一个实体或收集信息(登录、注册、发布、设置等场景)。
- 需要对输入的数据类型进行校验时(必填、格式、长度、正则、跨字段联动等)。
快速上手:受控数据域的第一个表单
Form 最核心的设计是接管被 name 字段包裹的子控件:自动注入 value(或 valuePropName 指定的属性)与 onChange(或 trigger 指定的属性),从而把所有字段数据收敛到自身的 store 中统一管理。完整示例见 basic demo,核心代码如下:
import { Button, Checkbox, Form, Input } from 'antd';
const onFinish = (values) => {
console.log('Success:', values);
};
const App = () => (
<Form
name="basic"
labelCol={{ span: 8 }}
wrapperCol={{ span: 16 }}
initialValues={{ remember: true }}
onFinish={onFinish}
onFinishFailed={(errorInfo) => console.log('Failed:', errorInfo)}
autoComplete="off"
>
<Form.Item
label="Username"
name="username"
rules={[{ required: true, message: 'Please input your username!' }]}
>
<Input />
</Form.Item>
<Form.Item name="remember" valuePropName="checked" label={null}>
<Checkbox>Remember me</Checkbox>
</Form.Item>
<Form.Item label={null}>
<Button type="primary" htmlType="submit">Submit</Button>
</Form.Item>
</Form>
);
提交时数据满足校验,触发 onFinish(values);校验失败则触发 onFinishFailed。注意 Checkbox 的真实值属性是 checked 而非 value,因此必须配合 valuePropName="checked"(其原理参见后文 FAQ)。一旦被 Form.Item 接管,数据流将遵循三条硬性规则:
- 不再需要也不应该用子控件
onChange做数据收集同步(仍可继续监听,但同步交给 Form)。需要全局监听值变化时用 Form 的onValuesChange。 - 不能用控件的
value/defaultValue设置表单域初始值,默认值应通过 Form 的initialValues设置。且initialValues不能被setState动态更新——动态改值必须用form.setFieldsValue。 - 不要用
setState维护受控字段,统一用form.setFieldsValue驱动。
Form:顶层表单组件
Form 同时承担「数据 store」与「布局容器」两个职责。从 Form.tsx 源码可见,其默认 layout 为 horizontal,渲染时通过 DisabledContextProvider、SizeContext.Provider、VariantContext.Provider 分别向下下发 disabled、size、variant,因此这些配置对树内所有 antd 控件「一键生效」。
API 一览
下表列出行内 FormProps 定义的完整可配置项(其中标注「全局配置」列的属性,还可以通过 ConfigProvider 的 form 命名空间统一设置):
| 参数 | 说明 | 类型 | 默认值 | 版本 | 全局配置 |
|---|---|---|---|---|---|
| classNames | 自定义组件内部各语义化结构的 class,支持对象或函数 | Record<SemanticDOM, string> | (info: { props }) => Record<SemanticDOM, string> |
- | 6.0.0 | 6.0.0 |
| colon | 配置 Form.Item 的 colon 默认值,表示是否显示 label 后的冒号(仅 layout 为 horizontal 时有效) |
boolean | true | 4.18.0 | 是 |
| disabled | 设置表单组件禁用,仅对 antd 组件有效 | boolean | false | 4.21.0 | × |
| component | 设置 Form 渲染元素,为 false 则不创建 DOM 节点 |
ComponentType | false |
form | - | × |
| fields | 通过状态管理(如 redux)控制表单字段,非强需求不推荐 | FieldData[] |
- | - | × |
| form | 经 Form.useForm() 创建的 form 控制实例,不提供时自动创建 |
FormInstance | - | - | × |
| feedbackIcons | Form.Item 有 hasFeedback 时自定义反馈图标 |
FeedbackIcons | - | 5.9.0 | × |
| initialValues | 表单默认值,仅初始化和重置时生效 | object | - | - | × |
| labelAlign | label 文本对齐方式 | left | right |
right |
- | 6.4.0 |
| labelWrap | label 文本是否可换行 | boolean | false | 4.18.0 | × |
| labelCol | label 标签布局,同 <Col>:{ span: 3, offset: 12 } 或 { sm: { span: 3, offset: 12 } } |
object | - | - | × |
| layout | 表单布局 | horizontal | vertical | inline |
horizontal |
- | × |
| name | 表单名称,会作为字段 id 前缀 |
string | - | - | × |
| preserve | 字段被删除时保留字段值,可用 getFieldsValue(true) 获取 |
boolean | true | 4.4.0 | × |
| requiredMark | 必选样式,可切换为必选或可选展示样式;此为 Form 级配置,Form.Item 无法单独配置 | boolean | 'optional' | ((label, { required }) => ReactNode) |
true | renderProps: 5.9.0 | 4.8.0 |
| scrollToFirstError | 提交失败自动滚动到第一个错误字段 | boolean | Options | { focus: boolean } |
false | focus: 5.24.0 | 5.2.0 |
| size | 字段组件尺寸(仅限 antd 组件) | small | medium | large |
- | - | × |
| styles | 自定义各语义化结构的行内 style,支持对象或函数 | Record<SemanticDOM, CSSProperties> | (info: { props }) => Record<...> |
- | - | 6.0.0 |
| tooltip | 配置提示属性 | TooltipProps & { icon?: ReactNode } |
- | 6.3.0 | 6.3.0 |
| validateMessages | 验证提示模板(详见下一节) | ValidateMessages | - | - | 4.0.0 |
| validateTrigger | 统一设置字段触发验证的时机 | string | string[] |
onChange |
4.3.0 | × |
| variant | 表单内控件变体 | outlined | borderless | filled | underlined |
outlined |
5.13.0(underlined: 5.24.0) |
5.19.0 |
| wrapperCol | 输入控件布局,用法同 labelCol | object | - | - | × |
| onFieldsChange | 字段更新时触发 | function(changedFields, allFields) |
- | - | × |
| onFinish | 提交且校验成功后触发 | function(values) |
- | - | × |
| onFinishFailed | 提交且校验失败后触发 | function({ values, errorFields, outOfDate }) |
- | - | × |
| onValuesChange | 字段值更新时触发 | function(changedValues, allValues) |
- | - | × |
| clearOnDestroy | 表单卸载时清空表单值 | boolean | false | 5.18.0 | × |
此外还支持原生
<form>除onSubmit外的所有属性(如autoComplete、id等)。在 Form.tsx 的FormProps中可见prefixCls、rootClassName等扩展字段;源码中useComponentConfig('form')会从 ConfigProvider 读取requiredMark、colon、tooltip、labelAlign等默认值并做合并,这正是「全局配置列」生效的机制。
默认校验消息 validateMessages
Form 为验证提供了默认错误提示信息(位于 locale/en_US.ts,含 required、types.*、string.*、number.*、array.*、pattern 等全部模板)。可通过 validateMessages 覆盖模板,最常见的是配置国际化提示:
const validateMessages = {
required: "'${name}' 是必选字段",
// ...
};
<Form validateMessages={validateMessages} />;
也可以通过 ConfigProvider 做全局统一配置:
const validateMessages = {
required: "'${name}' 是必选字段",
// ...
};
<ConfigProvider form={{ validateMessages }}>
<Form />
</ConfigProvider>;
从源码看,Form 渲染时会将 ConfigProvider 下发的 validateMessages 注入 FormProvider(见 Form.tsx),实现全树共享。
Form.Item:字段绑定、校验与布局的单元
Form.Item 用于数据双向绑定、校验、布局,是日常使用最频繁的子组件。主要 API 如下:
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|---|---|---|---|---|
| colon | 配合 label 使用,是否显示 label 后的冒号 |
boolean | true | - |
| dependencies | 设置依赖字段(见下节) | NamePath[] |
- | - |
| extra | 额外提示信息,与错误信息可同时出现 | ReactNode | - | - |
| getValueFromEvent | 设置如何把 event 的值转换成字段值 | (..args) => any |
- | - |
| getValueProps | 为子元素添加额外属性 | (value) => Record<string, any> |
- | 4.2.0 |
| hasFeedback | 配合 validateStatus 展示校验状态图标;可传 { icons: FeedbackIcons } |
boolean | { icons: FeedbackIcons } |
false | icons: 5.9.0 |
| help | 提示信息,不设置时按校验规则自动生成 | ReactNode | - | - |
| hidden | 是否隐藏字段(仍收集与校验) | boolean | false | 4.4.0 |
| htmlFor | 设置子元素 label 的 htmlFor |
string | - | - |
| initialValue | 子元素默认值,与 Form 的 initialValues 冲突时以 Form 为准 |
string | - | 4.2.0 |
| label | label 文本;不需要 label 又需与冒号对齐时设为 null |
ReactNode | - | null: 5.22.0 |
| labelAlign | 标签文本对齐方式 | left | right |
right |
- |
| labelCol | label 布局,同 <Col>;可通过 Form labelCol 统一设置(不作用于嵌套 Item),两者同时设置时以 Item 为准 |
object | - | - |
| messageVariables | 默认验证字段的信息替换 | Record<string, string> |
- | 4.7.0 |
| name | 字段名,支持数组 | NamePath | - | - |
| normalize | 组件获取值后先转换再放入 Form;不支持异步 | (value, prevValue, prevValues) => any |
- | - |
| noStyle | 为 true 时不带样式,作为纯字段控件;无自身 validateStatus 时会继承父级 Form.Item |
boolean | false | - |
| preserve | 字段被删除时保留字段值 | boolean | true | 4.4.0 |
| required | 必填样式;不设置则根据校验规则自动生成,设 false 可禁用该样式 |
boolean | - | - |
| rules | 校验规则(见 Rule) | Rule[] |
- | - |
| shouldUpdate | 自定义字段更新逻辑(见下节) | boolean | (prevValue, curValue) => boolean |
false | - |
| tooltip | 配置提示信息 | ReactNode | (TooltipProps & { icon?: ReactNode }) |
- | 4.7.0 |
| trigger | 收集字段值变更的时机 | string | onChange |
- |
| validateFirst | 某规则失败后是否停止后续规则;parallel 时并行校验 |
boolean | 'parallel' |
false | parallel: 4.5.0 |
| validateDebounce | 校验防抖延迟毫秒数 | number | - | 5.9.0 |
| validateStatus | 校验状态:success | warning | error | validating;不设置则自动生成 |
string | - | - |
| validateTrigger | 设置字段校验时机 | string | string[] |
onChange |
- |
| valuePropName | 子节点值属性名;Switch、Checkbox 应为 checked。是 getValueProps 的封装,自定义 getValueProps 后会失效 |
string | value |
- |
| wrapperCol | 输入控件布局,用法同 labelCol,优先级规则同 labelCol | object | - | - |
| layout | 表单项布局 | horizontal | vertical |
- | 5.18.0 |
注意 colon、requiredMark、size、disabled、variant 等更倾向在 Form 层统一设置,会通过 Context 下发到所有 Item 的控件上。
dependencies:字段间校验联动
当一个字段设置了 dependencies,它所依赖的字段更新时,该字段会自动触发更新与校验。典型场景是注册表单的「密码 / 确认密码」:「确认密码」依赖「密码」,修改密码后自动重跑确认逻辑。可运行示例见 form-dependencies demo:
<Form.Item
label="Confirm Password"
name="password2"
dependencies={['password']}
rules={[
{ required: true },
({ getFieldValue }) => ({
validator(_, value) {
if (!value || getFieldValue('password') === value) {
return Promise.resolve();
}
return Promise.reject(new Error('The new password that you entered do not match!'));
},
}),
]}
>
<Input />
</Form.Item>
注意:dependencies 不应与 shouldUpdate 混用,否则会带来更新逻辑混乱。此外它主要响应「用户交互触发」的字段更新;对于 setFieldsValue 触发的变化(如切换字段选项),应改用 shouldUpdate 或 useWatch(见 FAQ)。
FeedbackIcons:自定义校验反馈图标
当需要自定义 hasFeedback 的图标时使用:
type FeedbackIcons = (info: { status: ValidateStatus; errors: ReactNode; warnings: ReactNode }) =>
Record<ValidateStatus, ReactNode>;
shouldUpdate:精确控制渲染范围
Form 采用增量更新,只刷新被修改字段相关的组件以优化性能。大多数场景配合 dependencies 即可,但当「修改某字段后出现新字段选项」或「希望表单任意变化都让某区域重新渲染」时,用 shouldUpdate 改写 Form.Item 的更新逻辑。相关 issue:#34500。
当 shouldUpdate 为 true 时,Form 任意变化都会触发该 Form.Item 重渲染,且包裹的子组件必须由函数返回,否则不生效:
<Form.Item shouldUpdate>
{() => {
return <pre>{JSON.stringify(form.getFieldsValue(), null, 2)}</pre>;
}}
</Form.Item>
当 shouldUpdate 为函数时,每次数值更新都会携带新旧值供比较,非常适合「按值决定是否渲染额外字段」:
<Form.Item shouldUpdate={(prevValues, curValues) => prevValues.additional !== curValues.additional}>
{() => {
return (
<Form.Item name="other">
<Input />
</Form.Item>
);
}}
</Form.Item>
具体用法可分别参考 inline-login demo 与 control-hooks demo(后者用 shouldUpdate 在 gender === 'other' 时渲染额外输入项,其中 getFieldValue 由 renderProps 参数解构提供)。
messageVariables:替换校验信息中的变量
可通过 messageVariables 修改默认验证信息中 ${label}、${name} 等占位符的取值:
<Form>
<Form.Item
messageVariables={{ another: 'good' }}
label="user"
rules={[{ required: true, message: '${another} is required' }]}
>
<Input />
</Form.Item>
<Form.Item
messageVariables={{ label: 'good' }}
label={<span>user</span>}
rules={[{ required: true, message: '${label} is required' }]}
>
<Input />
</Form.Item>
</Form>
自 5.20.2 起,若希望保留 ${} 不被转译,用 \${} 跳过:
{ required: true, message: '${label} is convert, \\${label} is not convert' }
// 渲染结果:good is convert, ${label} is not convert
Form.List:动态增减与数组化管理
Form.List 为字段提供数组化管理,例如「多乘客」「多成员」列表。API 如下:
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|---|---|---|---|---|
| children | 渲染函数 | (fields: Field[], operation: { add, remove, move }, meta: { errors }) => ReactNode |
- | - |
| initialValue | 子元素默认值,与 Form initialValues 冲突时以 Form 为准 |
any[] |
- | 4.9.0 |
| name | 字段名,支持数组。List 本身也是字段,getFieldsValue() 默认返回其下所有值 |
NamePath | - | - |
| rules | 校验规则,仅支持自定义规则,需配合 Form.ErrorList 使用 | { validator, message }[] |
- | 4.7.0 |
基础结构:
<Form.List name="names">
{(fields) =>
fields.map((field) => (
<Form.Item {...field}>
<Input />
</Form.Item>
))
}
</Form.List>
注意:Form.List 下的字段不应各自配置 initialValue,应始终通过 Form.List 的 initialValue 或 Form 的 initialValues 配置。完整动态示例见 dynamic-form-item demo:它展示了通过 rules 校验 List 至少 2 项、fields.map 渲染输入行、add() 在尾部追加、add('The head item', 0) 指定插入位置、remove(field.name) 删除,以及用 <Form.ErrorList errors={errors} /> 展示 List 级错误。更复杂的动态嵌套字段、拖拽排序与无样式纯字段场景,可分别参考 dynamic-form-items demo、dynamic-form-items-drag-sorting demo 与 dynamic-form-items-no-style demo。
operation:List 操作函数
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|---|---|---|---|---|
| add | 新增表单项 | (defaultValue?: any, insertIndex?: number) => void |
insertIndex | 4.6.0 |
| move | 移动表单项 | (from: number, to: number) => void |
- | - |
| remove | 删除表单项 | (index: number | number[]) => void |
number[] | 4.5.0 |
Form.ErrorList:错误展示组件
4.7.0 新增,仅限配合 Form.List 的 rules 使用(参见 dynamic-form-item demo 中 errors 的展示方式):
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| errors | 错误列表 | ReactNode[] |
- |
Form.Provider:多表单联动
其下设置 name 的多个 Form 更新时,自动触发对应事件(示例见 form-context demo):
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| onFormChange | 子表单字段更新时触发 | function(formName: string, info: { changedFields, forms }) |
- |
| onFormFinish | 子表单提交时触发 | function(formName: string, info: { values, forms }) |
- |
<Form.Provider
onFormFinish={(name) => {
if (name === 'form1') {
// Do something...
}
}}
>
<Form name="form1">...</Form>
<Form name="form2">...</Form>
</Form.Provider>
FormInstance:命令式实例方法
Form.useForm() 返回的实例承载所有数据读写与校验操作。在 hooks/useForm.ts 中可看到它基于 @rc-component/form 的 rcForm 扩展出 scrollToField、focusField、getFieldInstance 等方法(scrollToField 先取字段 DOM,再调用 scroll-into-view-if-needed,focus: true 时滚动成功后聚焦)。完整方法如下:
| 名称 | 说明 | 类型 | 版本 |
|---|---|---|---|
| getFieldError | 获取指定字段的错误信息 | (name: NamePath) => string[] |
- |
| getFieldInstance | 获取字段实例 | (name: NamePath) => any |
4.4.0 |
| getFieldsError | 获取一组字段错误,返回数组 | (nameList?: NamePath[]) => FieldError[] |
- |
| getFieldsValue | 获取一组字段值,按结构返回;true 时返回含未注册字段的全部值 |
GetFieldsValue | - |
| getFieldValue | 获取指定字段值 | (name: NamePath) => any |
- |
| isFieldsTouched | 检查一组字段是否被操作过,allTouched: true 时要求全部被操作 |
(nameList?, allTouched?) => boolean |
- |
| isFieldTouched | 检查字段是否被操作过 | (name: NamePath) => boolean |
- |
| isFieldValidating | 检查字段是否正在校验 | (name: NamePath) => boolean |
- |
| resetFields | 重置字段到 initialValues |
(fields?: NamePath[]) => void |
- |
| scrollToField | 滚动到字段位置,可带 { focus: boolean } 聚焦 |
(name, options?) => void |
focus: 5.24.0 |
| setFields | 设置一组字段状态 | (fields: FieldData[]) => void |
- |
| setFieldValue | 设置单个字段值并重置其错误信息;不希望传入对象被修改请先克隆 | (name, value) => void |
4.22.0 |
| setFieldsValue | 设置字段值并重置错误;只想改 Form.List 中单项请用 setFieldValue |
(values) => void |
- |
| submit | 提交表单,等同于点击 submit 按钮 | () => void |
- |
| validateFields | 触发表单验证;recursive 时递归校验所有包含路径 |
(nameList?, config?) => Promise |
- |
validateFields 的配置与返回
export interface ValidateConfig {
// 5.5.0 新增。仅校验内容而不将错误信息展示到 UI 上。
validateOnly?: boolean;
// 5.9.0 新增。对 nameList 及其子路径递归校验。
recursive?: boolean;
// 5.11.0 新增。校验 dirty(touched + validated)字段,方便只校验用户操作过的字段。
dirty?: boolean;
}
返回示例:
validateFields()
.then((values) => {
/*
values:
{
username: 'username',
password: 'password',
}
*/
})
.catch((errorInfo) => {
/*
errorInfo:
{
values: { username: 'username', password: 'password' },
errorFields: [{ name: ['password'], errors: ['Please input your Password!'] }],
outOfDate: false,
}
*/
});
Hooks:命令式能力的四个入口
Form.useForm
type Form.useForm = (): [FormInstance];
创建 Form 实例,用于管理所有数据状态;配合 <Form form={form}> 使用。典型调用见 control-hooks demo(setFieldsValue、resetFields、提交/重置/填充联动)。组件复合结构由 index.tsx 挂载:Form.Item / Form.List / Form.ErrorList / Form.Provider / Form.useForm / Form.useFormInstance / Form.useWatch 均为静态成员。
Form.useFormInstance
type Form.useFormInstance = (): FormInstance;
4.20.0 新增。在嵌套子组件中获取当前上下文 Form 实例,避免逐层透传 form:
const Sub = () => {
const form = Form.useFormInstance();
return <Button onClick={() => form.setFieldsValue({})} />;
};
export default () => {
const [form] = Form.useForm();
return (
<Form form={form}>
<Sub />
</Form>
);
};
Form.useWatch
type Form.useWatch = (
namePath: NamePath | ((selector: (values: Store) => any)),
formInstance?: FormInstance | WatchOptions,
): Value;
5.12.0 新增 selector。用于直接响应式获取字段值,可与 useSWR 等数据请求联动,降低手动维护成本:
const Demo = () => {
const [form] = Form.useForm();
const userName = Form.useWatch('username', form);
const { data: options } = useSWR(`/api/user/${userName}`, fetcher);
return (
<Form form={form}>
<Form.Item name="username">
<AutoComplete options={options} />
</Form.Item>
</Form>
);
};
如果组件被包裹在 Form.Item 内部,可省略第二个参数,useWatch 会自动向上寻找最近 Form 实例。它默认只监听已注册字段,监听非注册字段需显式 preserve: true:
const age = Form.useWatch('age', { form, preserve: true });
Form.Item.useStatus
type Form.Item.useStatus = (): { status: ValidateStatus | undefined; errors: ReactNode[]; warnings: ReactNode[] };
4.22.0 新增,5.4.0 补充 errors、warnings。用于自定义控件内感知当前 Form.Item 的校验状态(上层无 Form.Item 时 status 为 undefined):
const CustomInput = ({ value, onChange }) => {
const { status, errors } = Form.Item.useStatus();
return (
<input
value={value}
onChange={onChange}
className={`custom-input-${status}`}
placeholder={(errors.length && errors[0]) || ''}
/>
);
};
export default () => (
<Form>
<Form.Item name="username">
<CustomInput />
</Form.Item>
</Form>
);
与其他取值方式的分工
Form 只对变更的 Field 刷新,避免整树重渲染的性能损耗,因此在 render 阶段无法通过 form.getFieldsValue 实时取到最新值。四类取值手段各有适用场景:
useWatch:特定字段的响应式访问,供当前组件 render/effect 直接消费。Field.renderProps:仅更新需要更新的部分,渲染性能最优。onValuesChange:当前组件不消费字段值时,将数据抛出,避免组件更新。Form.Item.useStatus:只关心校验状态与错误/警告文案,如自定义输入框的边框与提示。
关键接口与类型
NamePath
string | number | (string | number)[],用于定位字段路径,如 'username'、['user', 'age']。
GetFieldsValue 重载
getFieldsValue(nameList?: true | NamePath[], filterFunc?: FilterFunc):- 不传
nameList:返回所有注册字段(含 List 下所有值)。 - 传
true:返回 store 中所有值(含未注册字段,例如setFieldsValue设置的、尚无对应 Item 的值)。 - 传数组:返回规定路径的值,注意嵌套数组写法:
- 不传
// 单个路径
form.getFieldsValue([['user', 'age']]);
// 多个路径
form.getFieldsValue([
['user', 'age'],
['preset', 'account'],
]);
getFieldsValue({ filter?: FilterFunc }):按过滤函数取值。
FilterFunc
type FilterFunc = (meta: { touched: boolean; validating: boolean }) => boolean;
可用于只取被用户修改过的字段值等场景。
FieldData
| 名称 | 说明 | 类型 |
|---|---|---|
| errors | 错误信息 | string[] |
| warnings | 警告信息 | string[] |
| name | 字段名称 | NamePath[] |
| touched | 是否被用户操作过 | boolean |
| validating | 是否正在校验 | boolean |
| value | 字段值 | any |
Rule 校验规则
Rule 支持 object 配置,也支持 function 动态获取 Form 数据:type Rule = RuleConfig | ((form: FormInstance) => RuleConfig)。各配置项:
| 名称 | 说明 | 类型 | 版本 |
|---|---|---|---|
| defaultField | 仅在 type: 'array' 时有效,指定数组元素校验规则 |
rule | - |
| enum | 是否匹配枚举中的值(需 type: 'enum') |
any[] |
- |
| fields | 仅在 type: 'array' | 'object' 时有效,指定子元素校验规则 |
Record<string, rule> |
- |
| len | string 为长度、number 为确定值、array 为长度 | number | - |
| max | 需设置 type:string 最大长度 / number 最大值 / array 最大长度 |
number | - |
| message | 错误信息,不设置时按模板自动生成 | string | ReactElement |
- |
| min | 需设置 type,语义同 max |
number | - |
| pattern | 正则匹配 | RegExp | - |
| required | 是否必选 | boolean | - |
| transform | 校验前把值转换为目标值 | (value) => any |
- |
| type | 常见 string | number | boolean | url | email | tel 等 |
string | - |
| validateTrigger | 验证时机,必须是 Form.Item validateTrigger 的子集 |
string | string[] |
- |
| validator | 自定义校验,返回 Promise | (rule, value) => Promise |
- |
| warningOnly | 仅警告,不阻塞提交 | boolean | 4.17.0 |
| whitespace | 仅含空格视为不通过,仅在 type: 'string' 生效 |
boolean | - |
warningOnly 与动态校验可分别参考 warning-only demo、dynamic-rule demo;validator 的 Promise 用法见 register demo。
WatchOptions
| 名称 | 说明 | 类型 | 默认值 | 版本 |
|---|---|---|---|---|
| form | 指定 Form 实例 | FormInstance | 当前 context 中的 Form | 5.4.0 |
| preserve | 是否监视无对应 Form.Item 的字段 | boolean | false | 5.4.0 |
Semantic DOM 与主题变量
Form 支持以 classNames / styles 精确控制 root、label、content、help、helpItem、extra 等语义化节点(见 Form.tsx 的 FormSemanticType 定义),实际结构与示例见 _semantic.tsx,风格化用法见 style-class demo。组件级主题 Token 可参考 component-token demo。
FAQ:高频问题与底层原理
1. Segmented 为什么不能被 Form disabled 禁用?
Segmented 设计上是数据展示类而非表单控件类组件,行为更接近 Tabs,因此不受 Form 的 disabled 控制。讨论见 #54749。
2. Switch、Checkbox 为什么绑定不上数据?
Form.Item 默认绑定 value,而它们真实值属性是 checked,改用 valuePropName:
<Form.Item name="fieldA" valuePropName="checked">
<Switch />
</Form.Item>
3. name 为数组时的转换规则?
按顺序填充路径;当存在数字且 store 中没有该字段时自动转数组,因此需要数字作为 key 时应写成字符串,如 ['1', 'name']。
4. 为何在 Modal 中调用 form 控制台报错("Instance created by useForm is not connect to any Form element")?
调用时 Modal 尚未初始化、form 未关联任何 Form。给 Modal 设置 forceRender 预渲染即可。
5. 为什么 Form.Item 下子组件 defaultValue 不生效?
设置 name 后子组件转为受控模式,defaultValue 自然失效,应通过 Form 的 initialValues 设置默认值。
6. 为什么第一次调用 ref 的 Form 为空?
ref 仅在节点被加载后才被赋值,这是 React 的标准机制。
7. 为什么 resetFields 会重新 mount 组件?
resetFields 会重置整个 Field,子组件随之重新 mount,从而清除自定义组件内部的副作用(异步数据、本地状态等)。
8. Form 的 initialValues 与 Item 的 initialValue 区别?
优先使用 Form 的 initialValues,仅在动态字段场景使用 Item 的 initialValue。默认值优先级:Form initialValues 最高 > Field initialValue;多个同 name Item 均设 initialValue 时该值不生效。
9. 为什么 getFieldsValue 初次渲染拿不到值?
它默认只返回已收集(已渲染注册)的字段数据;初次渲染 Form.Item 尚未挂载,需用 getFieldsValue(true) 获取全部数据。
10. 为什么 setFieldsValue 设为 undefined 时有的组件不重置为空?
React 中值从确定值变为 undefined 意味着从受控转为非受控,展示值不会重置(Form store 内的值实际已变)。用带默认值的 HOC 保持受控:
const MyInput = ({ value = '', ...rest }) => <input value={value} {...rest} />;
<Form.Item name="my">
<MyInput />
</Form.Item>;
11. 为什么设置 rules 后 onFieldsChange 触发三次?
字段状态除值外还包括校验态,变化过程为:Trigger value change → Rule validating → Rule validated,因此 isFieldValidating 会经历 false → true → false。
12. 为什么 Form.List 不支持 label、还需用 ErrorList?
Form.List 是 renderProps,内部样式自由,预设 label/error 节点难以配合;需要 antd 样式 label 时请用外层 Form.Item 包裹。
13. 为什么 dependencies 对 Form.List 下的字段无效?
依赖路径必须包含 Form.List 本身的 name,如:
<Form.List name="users">
{(fields) =>
fields.map((field) => (
<React.Fragment key={field.key}>
<Form.Item name={[field.name, 'name']} {...someRest1} />
<Form.Item name={[field.name, 'age']} {...someRest1} />
</React.Fragment>
))
}
</Form.List>
此时依赖写法是 ['users', 0, 'name']。
14. 为什么 normalize 不能是异步方法?
React 中异步更新会导致受控组件交互异常:onChange 触发后值不会立即回写,组件呈「假死」状态。需要异步变更请用自定义组件内部实现异步状态。
15. scrollToFirstError 和 scrollToField 失效?
- 使用了自定义控件:自 5.17.0 起滚动优先使用控件转发的 ref 元素,请优先把
ref转发给真正的表单元素;滚动依赖元素上的id,自定义控件未把id赋到正确元素会失效(相关 issue:#28370、#27994)。 - 页面内多个表单且
name重复:滚动可能定位到另一表单的同名项,需给各 Form 设置不同name。
16. 为什么不直接用 ref 绑定滚动元素?
自定义组件不支持 ref 时 Form 拿不到真实 DOM,而用 Class 包装调用 findDOMNode 在 React Strict Mode 下会告警,因此改用 id 定位元素。
17. setFieldsValue 不会触发 onFieldsChange / onValuesChange?
是的,change 事件仅由用户交互触发,以防在 change 回调内调用 setFieldsValue 造成死循环;组件内消费请用 useWatch 或 Field.renderProps。
18. 为什么 dependencies 不响应 setFieldsValue 触发的更新?
dependencies 面向用户交互触发的字段间校验联动;需要按 setFieldsValue 后的值渲染内容或切换选项时用 shouldUpdate 或 useWatch。
19. 为什么 Form.Item 嵌套子组件后不更新表单值?
Form.Item 只向「直接子元素」注入 value/onChange,被包裹后属性无法穿透:
{/* 不会生效 */}
<Form.Item name="input">
<div>
<h3>I am a wrapped Input</h3>
<Input />
</div>
</Form.Item>
改为 HOC 自定义组件形式:
const MyInput = (props) => (
<div>
<h3>I am a wrapped Input</h3>
<Input {...props} />
</div>
);
<Form.Item name="input">
<MyInput />
</Form.Item>;
20. 为什么点击 label 会更改组件状态?
label 使用原生 HTML label 包裹控件以实现点击聚焦,这是提升可访问性的标准行为。若确实需要解除,可设置 htmlFor={null}:
- <Form.Item name="switch" label="Switch">
+ <Form.Item name="switch" label="Switch" htmlFor={null}>
<Switch />
</Form.Item>
延伸阅读
想进一步把 Form 用「活」,可以依次深入仓库内示例与源码:
- 布局三件套:
horizontal / vertical / inline切换见 layout demo、layout-multiple demo;必选/可选样式与requiredMark见 required-mark demo。 - 数据管理:全局数据存于上层组件见 global-state demo;多表单联动见 form-context demo。
- 校验进阶:
validateTrigger见 validate-trigger demo;仅校验不提交见 validate-only demo;自定义校验见 validate-static demo;getValueProps + normalize见 getValueProps-normalize demo。 - 典型业务形态:内联登录栏 inline-login、登录框 login、注册新用户 register、高级搜索 advanced-search、Modal 内新建表单 form-in-modal。
以上需求都建立在同一个底层能力之上:Form 把「数据、校验、布局」收敛为一份受控状态。理解了 FormInstance 与增量更新的关系,再配合 dependencies / shouldUpdate / useWatch 三种联动手段,即可在几乎所有复杂场景下写出高性能、易维护的表单代码。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0629
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python08
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00