Form.Item相关
form.item 带上name的表示为受控组件,不能通过onChange直接赋值
Form.item支持嵌套,但是一个Form.item只能包裹一个输入
如果你需要 antd 样式的 label,可以通过外部包裹 Form.Item 来实现
Form.item如果包裹CheckBox\Switch,需要添加属性 valuePropName="checked"
<Form.Item>
<Form.Item name="remember" valuePropName="checked" noStyle>
<Checkbox>Remember me</Checkbox>
</Form.Item>
<a className="login-form-forgot" href="">
Forgot password
</a>
</Form.Item>
Form.Item noStyle
多用于嵌套Form.Item的场景中
<Form.Item label="导出路径" {...filePathItemRadioLayout}>
<Input.Group compact>
<Form.Item
name={'filePath'}
noStyle
rules={[{required: true, message: '请选择导出文件路径'}]}
>
<Input style={{width: 'calc(100% - 66px)'}} />
</Form.Item>
<Button onClick={handleSelectPath}>选择</Button>
</Input.Group>
</Form.Item>
<Form.Item label="InputNumber">
<Form.Item name="input-number" noStyle>
<InputNumber min={1} max={10} />
</Form.Item>
<span className="ant-form-text"> machines</span>
</Form.Item>
form.getFieldValue
可以在form.item内部使用getFieldValue,获取对应字段名的值
<Form.Item
label="User List"
shouldUpdate={(prevValues, curValues) => prevValues.users !== curValues.users}
>
{({ getFieldValue }) => {
const users: UserType[] = getFieldValue('users') || [];
return users.length ? (
<ul>
{users.map((user, index) => (
<li key={index} className="user">
<Avatar icon={<UserOutlined />} />
{user.name} - {user.age}
</li>
))}
</ul>
) : (
<Typography.Text className="ant-form-text" type="secondary">
( <SmileOutlined /> No user yet. )
</Typography.Text>
);
}}
</Form.Item>
确认密码:首先保证必填; 其次若值与密码一致,则返回成功
<Form.Item
name="confirm"
label="Confirm Password"
dependencies={['password']}
hasFeedback
rules={[
{
required: true,
message: 'Please confirm your password!',
},
({ getFieldValue }) => ({
validator(_, value) {
if (!value || getFieldValue('password') === value) {
return Promise.resolve();
}
return Promise.reject(new Error('The two passwords that you entered do not match!'));
},
}),
]}
>
<Input.Password />
</Form.Item>
Form.item 设置 shouldUpdate 自定义字段更新逻辑
Form 通过增量更新方式,只更新被修改的字段相关组件以达到性能优化目的。
大部分场景下,你只需要编写代码或者与 dependencies 属性配合校验即可。
而在某些特定场景,例如修改某个字段值后出现新的字段选项、或者纯粹希望表单任意变化都对某一个区域进行渲染。你可以通过 shouldUpdate 修改 Form.Item 的更新逻辑。
当 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>
Form.Item Row Col
<Form.Item label="Captcha" extra="We must make sure that your are a human.">
<Row gutter={8}>
<Col span={12}>
<Form.Item
name="captcha"
noStyle
rules={[{ required: true, message: 'Please input the captcha you got!' }]}
>
<Input />
</Form.Item>
</Col>
<Col span={12}>
<Button>Get captcha</Button>
</Col>
</Row>
</Form.Item>
Form.Item 设置tooltip
<Form.Item label="Field A" required tooltip="This is a required field">
<Input placeholder="input placeholder" />
</Form.Item>
<Form.Item
label="Field B"
tooltip={{ title: 'Tooltip with customize icon', icon: <InfoCircleOutlined /> }}
>
<Input placeholder="input placeholder" />
</Form.Item>
Form.Item是 文件上传的话, valuePropName="fileList"
getValueFromEvent 设置如何将 event 的值转换成字段值
const normFile = (e: any) => {
console.log('Upload event:', e);
if (Array.isArray(e)) {
return e;
}
return e?.fileList;
};
<Form.Item
name="upload"
label="Upload"
valuePropName="fileList"
getValueFromEvent={normFile}
extra="longgggggggggggggggggggggggggggggggggg"
>
<Upload name="logo" action="/upload.do" listType="picture">
<Button icon={<UploadOutlined />}>Click to upload</Button>
</Upload>
</Form.Item>
自动提示 AutoComplete
const [autoCompleteResult, setAutoCompleteResult] = useState<string[]>([]);
const onWebsiteChange = (value: string) => {
if (!value) {
setAutoCompleteResult([]);
} else {
setAutoCompleteResult(['.com', '.org', '.net'].map(domain => `${value}${domain}`));
}
};
const websiteOptions = autoCompleteResult.map(website => ({
label: website,
value: website,
}));
<Form.Item
name="website"
label="Website"
rules={[{ required: true, message: 'Please input website!' }]}
>
<AutoComplete options={websiteOptions} onChange={onWebsiteChange} placeholder="website">
<Input />
</AutoComplete>
</Form.Item>
表单校验
Form.Item 设置的rules是一个数组
<Form.Item
name="email"
label="E-mail"
rules={[
{
type: 'email',
message: 'The input is not valid E-mail!',
},
{
required: true,
message: 'Please input your E-mail!',
},
]}
>
<Input />
</Form.Item>
whitespace: true 如果字段仅包含空格则校验不通过,只在 type: 'string' 时生效
<Form.Item
name="nickname"
label="Nickname"
tooltip="What do you want others to call you?"
rules={[{ required: true, message: 'Please input your nickname!', whitespace: true }]}
>
<Input />
</Form.Item>
自定义 validator
你可以选择通过 async 返回一个 promise 或者使用 try...catch 进行错误捕获
validator: async (rule, value) => {
throw new Error('Something wrong!');
}
// or
validator(rule, value, callback) => {
try {
throw new Error('Something wrong!');
} catch (err) {
callback(err);
}
}
Form.Item dependencies 设置依赖字段
当字段间存在依赖关系时使用。如果一个字段设置了 dependencies 属性。
那么它所依赖的字段更新时,该字段将自动触发更新与校验。
一种常见的场景,就是注册用户表单的“密码”与“确认密码”字段。
“确认密码”校验依赖于“密码”字段,设置 dependencies 后,“密码”字段更新会重新触发“校验密码”的校验逻辑
<Form.Item
name="confirm"
label="Confirm Password"
dependencies={['password']}
hasFeedback
rules={[
{
required: true,
message: 'Please confirm your password!',
},
({ getFieldValue }) => ({
validator(_, value) {
if (!value || getFieldValue('password') === value) {
return Promise.resolve();
}
return Promise.reject(new Error('The two passwords that you entered do not match!'));
},
}),
]}
>
<Input.Password />
</Form.Item>
dependencies 不应和 shouldUpdate 一起使用,因为这可能带来更新逻辑的混乱。
hasFeedback 配合 validateStatus 属性使用,展示校验状态图标,建议只配合 Input 组件使用
校验项是数组
<Form.Item
name="residence"
label="Habitual Residence"
rules={[
{ type: 'array', required: true, message: 'Please select your habitual residence!' },
]}
>
<Cascader options={residences} />
</Form.Item>
手动触发异步校验
const onCheck = async () => {
try {
const values = await form.validateFields();
console.log('Success:', values);
} catch (errorInfo) {
console.log('Failed:', errorInfo);
}
};
form.validateFields()
.then(values => {
form.resetFields();
onCreate(values);
})
.catch(info => {
console.log('Validate Failed:', info);
});
form.validateFields 返回示例
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,
}
*/
});
Form相关
Form layout="inline" 会将每一项输入框在一行进行排列
const [form] = Form.useForm();
需要将form关联到Form标签上
form.setFieldsValue
支持对象深层次修改单个属性,例如
const toggleAllSelected = (checked: boolean) => {
form.setFieldsValue({
originChannel: {checked: checked},
extractChromatogram: {checked: checked},
extractMassSpectrum: {checked: checked},
})
}
form.resetFields();
重置一组字段到 initialValues
设置确认按钮在表单所有字段未都被触摸过或表单校验有误时禁用
shouldUpdate 表单值改变就要刷新
form.isFieldsTouched(true) 检查一组字段是否被用户操作过,allTouched 为 true 时检查是否所有字段都被操作过
!form.isFieldsTouched(true) 检查是否所有字段没有都被操作过
form.getFieldsError() 获取一组字段名对应的错误信息,返回为数组形式
<Form.Item shouldUpdate>
{() => (
<Button
type="primary"
htmlType="submit"
disabled={
!form.isFieldsTouched(true) ||
!!form.getFieldsError().filter(({ errors }) => errors.length).length
}
>
Log in
</Button>
)}
</Form.Item>
{...formItemLayout} {...tailFormItemLayout}
const formItemLayout = {
labelCol: {
xs: { span: 24 },
sm: { span: 8 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 },
},
};
const tailFormItemLayout = {
wrapperCol: {
xs: {
span: 24,
offset: 0,
},
sm: {
span: 16,
offset: 8,
},
},
};
Form.useFormInstance
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
4.20.0 新增,用于直接获取 form 中字段对应的值。通过该 Hooks 可以与诸如 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 仅会对变更的 Field 进行刷新,从而避免完整的组件刷新可能引发的性能问题。
因而你无法在 render 阶段通过 form.getFieldsValue 来实时获取字段值,而 useWatch 提供了一种特定字段访问的方式,从而使得在当前组件中可以直接消费字段的值。
同时,如果为了更好的渲染性能,你可以通过 Field 的 renderProps 仅更新需要更新的部分。
而当当前组件更新或者 effect 都不需要消费字段值时,则可以通过 onValuesChange 将数据抛出,从而避免组件更新。
FAQ
为什么 Form.Item 下的子组件 defaultValue 不生效?#
当你为 Form.Item 设置 name 属性后,子组件会转为受控模式。
因而 defaultValue 不会生效。你需要在 Form 上通过 initialValues 设置默认值。
Form 的 initialValues 与 Item 的 initialValue 区别?#
在大部分场景下,我们总是推荐优先使用 Form 的 initialValues。
只有存在动态字段时你才应该使用 Item 的 initialValue。
默认值遵循以下规则:
- Form 的
initialValues拥有最高优先级 - Field 的
initialValue次之 *. 多个同nameItem 都设置initialValue时,则 Item 的initialValue不生效
为什么字段设置 rules 后更改值 onFieldsChange 会触发三次?#
字段除了本身的值变化外,校验也是其状态之一。 因而在触发字段变化会经历以下几个阶段:
- Trigger value change
- Rule validating
- Rule validated
在触发过程中,调用 isFieldValidating 会经历 false > true > false 的变化过程。