* init form * first demo * add normal login * add style * webit * support nest errors * beauti form errors * use onReset * modal demo * add list demo * match key of errors logic * date demo * customize component * moving style * add status style * without form create * add demos * add inline style * clean up legacy * fix drawer demo * mention * fix edit-row * editable table cell * update mentions demo * fix some test case * fix upload test * fix lint * part of doc * fix ts * doc update * rm react 15 * rm config * enhance test coverage * clean up * fix FormItem context pass logic * add more demo * en to build * update demo * update demo & snapshot * more doc * update list doc * update doc * update demo to display condition render * update snapshot * add provider doc * support configProvider * more doc about validateMessages * more description * more and more doc * fix typo * en doc * Form.List doc * m v3 -> v4 * add skip
6.2 KiB
title | skip |
---|---|
Migrate to v4 | true |
Remove Form.create
Form of v4 is not need create context by Form.create()
. Form is now have own data scope and you don't need getFieldDecorator
anymore. Just use Form.Item directly:
// antd v3
const Demo = ({ form: { getFieldDecorator } }) => (
<Form>
<Form.Item>
{getFieldDecorator('username', {
rules: [{ required: true }],
})(<Input />)}
</Form.Item>
</Form>
);
const WrappedDemo = Form.create()(Demo);
To:
// antd v4
const Demo = () => (
<Form>
<Form.Item name="username" rules={[{ required: true }]}>
<Input />
</Form.Item>
</Form>
);
Since Form.create()
is removed, origin onFieldsChange
or other methods moved to Form, and control by fields
. ref example。
Form control
If you want to control form, you can use Form.useForm()
to create Form instance for operation:
// antd v3
const Demo = ({ form: { setFieldsValue } }) => {
React.useEffect(() => {
setFieldsValue({
username: 'Bamboo',
});
}, []);
return (
<Form>
<Form.Item>
{getFieldDecorator('username', {
rules: [{ required: true }],
})(<Input />)}
</Form.Item>
</Form>
);
};
const WrappedDemo = Form.create()(Demo);
To:
// antd v4
const Demo = () => {
const [form] = Form.useForm();
React.useEffect(() => {
form.setFieldsValue({
username: 'Bamboo',
});
}, []);
return (
<Form form={form}>
<Form.Item name="username" rules={[{ required: true }]}>
<Input />
</Form.Item>
</Form>
);
};
For class component, you can use ref
to access instance:
// antd v4
class Demo extends React.Component {
formRef = React.createRef();
componentDidMount() {
this.formRef.setFieldsValue({
username: 'Bamboo',
});
}
render() {
return (
<Form ref={formRef}>
<Form.Item name="username" rules={[{ required: true }]}>
<Input />
</Form.Item>
</Form>
);
}
}
If you don't want to use the Item style, you can use inline
prop to remove it:
// antd v3
const Demo = ({ form: { setFieldsValue } }) => {
return <Form>{getFieldDecorator('username')(<Input />)}</Form>;
};
const WrappedDemo = Form.create()(Demo);
To:
// antd v4
const Demo = () => {
return (
<Form>
<Form.Item name="username" inline>
<Input />
</Form.Item>
</Form>
);
};
Linkage with field
New Form use incremental update which only update related field. So if there are some linkage between fields or update with whole form. You can use dependencies
or shouldUpdate
to handle that.
replace onSubmit with onFinish
You need to listen onSubmit
and call validateFields
to handle validation in old Form. New Form provides onFinish
which only trigger when validation is passed:
// antd v3
const Demo = ({ form: { getFieldDecorator, validateFields } }) => {
const onSubmit = e => {
e.preventDefault();
validateFields((err, values) => {
if (!err) {
console.log('Received values of form: ', values);
}
});
};
return (
<Form onSubmit={onSubmit}>
<Form.Item>
{getFieldDecorator('username', {
rules: [{ required: true }],
})(<Input />)}
</Form.Item>
</Form>
);
};
const WrappedDemo = Form.create()(Demo);
To:
// antd v4
const Demo = () => {
const onFinish = values => {
console.log('Received values of form: ', values);
};
return (
<Form onFinish={onFinish}>
<Form.Item name="username" rules={[{ required: true }]}>
<Input />
</Form.Item>
</Form>
);
};
Initialization
Besides, we move initialValue
into Form to avoid field with same name both using initialValue
to cause conflict:
// antd v3
const Demo = ({ form: { getFieldDecorator } }) => (
<Form>
<Form.Item>
{getFieldDecorator('username', {
rules: [{ required: true }],
initialValue: 'Bamboo',
})(<Input />)}
</Form.Item>
</Form>
);
const WrappedDemo = Form.create()(Demo);
To:
// antd v4
const Demo = () => (
<Form initialValues={{ username: 'Bamboo' }}>
<Form.Item name="username" rules={[{ required: true }]}>
<Input />
</Form.Item>
</Form>
);
In v3, modifying the initialValue
of un-operated field will update the field value synchronously, which is a bug. However, since it has been used as a feature for a long time, we have not fixed it. In v4, the bug has been fixed. initialValues
only takes effect when initializing and resetting the form.
Remove field no longer removes field value
In v3, we will clean up the values when the fields removed. However, it has been found that clearing fields can cause greater inconvenience than retention in practice. For example like some switching operations, the user has to retain the field value in a hidden way. Therefore, the new version of Form will always keep the fields value, and the validation rules for removing field will be ignored. So preserve
is no longer needed.
Nested field paths using arrays
In the past versions we used .
to represent nested paths (such as user.name
to represent { user: { name: '' } }
). However, in some backend systems, .
is also included in the variable name. This causes the user to need additional code to convert, so in the new version, nested paths are represented by arrays to avoid unexpected behavior (eg ['user', 'name']
).
Therefore, paths returned by methods such as getFieldsError
are always in an array form for the user to handle:
form.getFieldsError();
/*
[
{ name: ['user', 'name'], errors: [] },
{ name: ['user', 'age'], errors: ['Some error message'] },
]
*/
Remove callback in validateFields
validateFields
will return a Promise, so you can handle the error with async/await
or then/catch
. It is no longer necessary to determine if errors
is empty:
// antd v3
validateFields((err, value) => {
if (!err) {
// Do something with value
}
});
To
// antd v4
validateFields().then(values => {
// Do something with value
});