antd@4 中改动最大的就是 form 组件,新的特性让我们减少了很多工作量,但是它的行为与3.0 中有一些不同的地方需要注意一下,在接近三个月的使用中,我总结除了一些小技巧,所以写了这个小文章,希望可以帮助大家减少迁移时的小坑。
如何迁移antd@4 可以看我前面的文章
form.getField Value 不生效了
在 antd@4 中,引入了 useForm 来减少替代 form.create,同时对 hooks 使用更加友好,所以我们一般按照习惯一把梭。
const Page = () => {
const [form] = Form.useForm();
return (
<Form form={form}>
<FormItem name="target" label="监控对象">
<Select>
<Option value="0">表一</Option>
<Option value="1">表二</Option>
</Select>
</FormItem>
<FormItem name="target" label="监控对象">
{form.getFieldValue('target') === '0' ? <Input /> : <Input.TextArea />}
</FormItem>
</Form>
);
};
export default Page;
但是查看效果的时候的会发现 form.getFieldValue('target')
根本取不到值,这个其实是因为 form 为了优化性能,减少 render 的次数,getFieldValue
并不会触发组件的 render,导致我们去取不到新的值。当然 set 开始的属性功能还是都可以用的。
为了实现上面的功能,我们需要这样写,比较麻烦的是 shouldUpdate
与 name
不能同时出现,否则会无法显示任何 dom 元素。
<FormItem label="监控对象" shouldUpdate>
{({ getFieldValue }) => {
return (
<FormItem name="target" label="监控对象">
{getFieldValue('target') === '0' ? <Input /> : <Input.TextArea />}
</FormItem>
);
}}
</FormItem>
如果你的表单比较小,或者对性能不在乎,你可以直接这样
const Page = () => {
const [form] = Form.useForm();
return (
<Form form={form}>
<FormItem shouldUpdate noStyle>
{({ getFieldValue }) => (
<>
<FormItem name="target" label="监控对象">
<Select>
<Option value="0">表一</Option>
<Option value="1">表二</Option>
</Select>
</FormItem>
<FormItem name="target" label="监控对象">
{getFieldValue('target') === '0' ? <Input /> : <Input.TextArea />}
</FormItem>
</>
)}
</FormItem>
</Form>
);
};
export default Page;
直接在最外面包裹一些 <FormItem shouldUpdate noStyle />
,虽然性能会很差,但是表现与 antd@3 基本相同。
initialValue 没了(又加回来了)
在 antd@4 中 initialValue
被删除,修改为 initialValues
来统一管理。
<Form
form={form}
initialValues={{
target: '0',
input: '详情',
}}
/>;
在我们使用的过程中经常无法分清 initialValue
和 setFieldsValue
的区别,很多时候两者是可以公用的,但是有些时候却会觉得有些奇怪,为什么无法生效呢?以下几种情况,表单分别会显示什么值,reset 之后呢?
const [initialValues, setInitialValues] = useState({});
useEffect(() => {
setInitialValues({
target: '0',
input: '详情',
});
}, []);
return (
<Form form={form} initialValues={initialValues}>
------------------------------------------------------------------------------------------
const [initialValues, setInitialValues] = useState({
target: '2',
input: '详情5',
});
useEffect(() => {
setInitialValues({
target: '0',
input: '详情',
});
}, []);
return (
<Form form={form} initialValues={initialValues}>
------------------------------------------------------------------------------------------
useEffect(() => {
form.setFieldsValue({
target: '0',
input: '详情2',
});
}, []);
return (
<Form
form={form}
initialValues={{
target: '0',
input: '详情',
}}
>
- 如果在渲染之前就能确定,使用 initialValues
- 如果需要计算,但是希望 reset 之后能显示出来,使用 initialValues state 加 setFieldsValue
- 如果只需要赋值,比如修改某些数据,使用 setFieldsValue
另外值得注意的是 defaultValue
现在不能和 name 共存。详细可以看官网的说明。
initialValue 虽然又被加了回来,但是优先级非常低,initialValues 仍然是优先级最高的,如果不是非常必要,尽量使用 initialValues。
validator 修改为 async
validator 原来一直使用 callback 来做异步,在 4 中,终于支持了更优雅的 async。
<FormItem
name="target"
label="监控对象"
rules={[
{
validator: async (_, value) => {
if (value === '1') {
return;
}
throw new Error('只允许选择表一哦');
},
},
]}
/>;
form 未绑定
如果在控制台中看到这个错误,一般是 Form.useForm()
的 生成的 form 没有使用。
form.validateFields() 无法获取值
这段代码其实是错误的
const submit = () => {
const msg = form.validateFields();
request('url', {
data: msg,
});
};
应该写这样 :
const submit = async () => {
try {
const msg = await form.validateFields();
request('url', {
data: msg,
});
} catch (e) {
console.log('我失败了', e);
}
};
很甜的语法糖
antd 中提供一些语法糖来方便做到我们原来需要自己实现的功能。
Form.List
这种列表在我们的项目中经常会遇到,原来我们需要写一堆代码。
<Form.List name="names">
{fields => (
<div>
{fields.map(field => (
<Form.Item {...field}>
<Input />
</Form.Item>
))}
</div>
)}
</Form.List>
多表单的互动
Form.Provider 提供了一个更简单的多表单互动方案,有效解决了 分布表单应该用一个表单还是多个表单的历史性难题。
<Form.Provider
onFormChange={()=>{
// Do something...
}}
onFormFinish={name => {
if (name === 'form1') {
// Do something...
}
}}
>
<Form name="form1">...</Form>
<Form name="form2">...</Form>
</Form.Provider>
验证信息的模板
Form 的 validateMessages 支持了验证信息模板的功能,这个功能减少很多的工作量。
const validateMessages = {
required: "'${name}' 是必选字段",
// ...
};
<Form validateMessages={validateMessages} />;
很多时候 name 是英文,这时候可以用
const validateMessages = {
required: "'${label}' 是必选字段",
// ...
};
// 邪道用法,但是效果不错
const validateMessages = {
required: "此项为必填项",
// ...
};
<Form validateMessages={validateMessages} />;
valuePropName=”fileList”
有些特殊的组件没有 value ,需要用 valuePropName
修改触发的值。
<Form.Item name="switch" label="Switch" valuePropName="checked">
<Switch />
</Form.Item>
<Form.Item
name="upload"
label="Upload"
valuePropName="fileList"
getValueFromEvent={normFile}
>
<Upload name="logo" action="/upload.do" listType="picture">
<Button>
<UploadOutlined /> Click to upload
</Button>
</Upload>
</Form.Item>;