antd@4 中改动最大的就是 form 组件,新的特性让我们减少了很多工作量,但是它的行为与3.0 中有一些不同的地方需要注意一下,在接近三个月的使用中,我总结除了一些小技巧,所以写了这个小文章,希望可以帮助大家减少迁移时的小坑。

如何迁移antd@4 可以看我前面的文章

form.getField Value 不生效了

在 antd@4 中,引入了 useForm 来减少替代 form.create,同时对 hooks 使用更加友好,所以我们一般按照习惯一把梭。

  1. const Page = () => {
  2. const [form] = Form.useForm();
  3. return (
  4. <Form form={form}>
  5. <FormItem name="target" label="监控对象">
  6. <Select>
  7. <Option value="0">表一</Option>
  8. <Option value="1">表二</Option>
  9. </Select>
  10. </FormItem>
  11. <FormItem name="target" label="监控对象">
  12. {form.getFieldValue('target') === '0' ? <Input /> : <Input.TextArea />}
  13. </FormItem>
  14. </Form>
  15. );
  16. };
  17. export default Page;

但是查看效果的时候的会发现 form.getFieldValue('target') 根本取不到值,这个其实是因为 form 为了优化性能,减少 render 的次数,getFieldValue 并不会触发组件的 render,导致我们去取不到新的值。当然 set 开始的属性功能还是都可以用的。

为了实现上面的功能,我们需要这样写,比较麻烦的是 shouldUpdatename 不能同时出现,否则会无法显示任何 dom 元素。

  1. <FormItem label="监控对象" shouldUpdate>
  2. {({ getFieldValue }) => {
  3. return (
  4. <FormItem name="target" label="监控对象">
  5. {getFieldValue('target') === '0' ? <Input /> : <Input.TextArea />}
  6. </FormItem>
  7. );
  8. }}
  9. </FormItem>

如果你的表单比较小,或者对性能不在乎,你可以直接这样

  1. const Page = () => {
  2. const [form] = Form.useForm();
  3. return (
  4. <Form form={form}>
  5. <FormItem shouldUpdate noStyle>
  6. {({ getFieldValue }) => (
  7. <>
  8. <FormItem name="target" label="监控对象">
  9. <Select>
  10. <Option value="0">表一</Option>
  11. <Option value="1">表二</Option>
  12. </Select>
  13. </FormItem>
  14. <FormItem name="target" label="监控对象">
  15. {getFieldValue('target') === '0' ? <Input /> : <Input.TextArea />}
  16. </FormItem>
  17. </>
  18. )}
  19. </FormItem>
  20. </Form>
  21. );
  22. };
  23. export default Page;

直接在最外面包裹一些 <FormItem shouldUpdate noStyle /> ,虽然性能会很差,但是表现与 antd@3 基本相同。

initialValue 没了(又加回来了)

在 antd@4 中 initialValue 被删除,修改为 initialValues 来统一管理。

  1. <Form
  2. form={form}
  3. initialValues={{
  4. target: '0',
  5. input: '详情',
  6. }}
  7. />;

在我们使用的过程中经常无法分清 initialValuesetFieldsValue 的区别,很多时候两者是可以公用的,但是有些时候却会觉得有些奇怪,为什么无法生效呢?以下几种情况,表单分别会显示什么值,reset 之后呢?

  1. const [initialValues, setInitialValues] = useState({});
  2. useEffect(() => {
  3. setInitialValues({
  4. target: '0',
  5. input: '详情',
  6. });
  7. }, []);
  8. return (
  9. <Form form={form} initialValues={initialValues}>
  10. ------------------------------------------------------------------------------------------
  11. const [initialValues, setInitialValues] = useState({
  12. target: '2',
  13. input: '详情5',
  14. });
  15. useEffect(() => {
  16. setInitialValues({
  17. target: '0',
  18. input: '详情',
  19. });
  20. }, []);
  21. return (
  22. <Form form={form} initialValues={initialValues}>
  23. ------------------------------------------------------------------------------------------
  24. useEffect(() => {
  25. form.setFieldsValue({
  26. target: '0',
  27. input: '详情2',
  28. });
  29. }, []);
  30. return (
  31. <Form
  32. form={form}
  33. initialValues={{
  34. target: '0',
  35. input: '详情',
  36. }}
  37. >
  • 如果在渲染之前就能确定,使用 initialValues
  • 如果需要计算,但是希望 reset 之后能显示出来,使用 initialValues state 加 setFieldsValue
  • 如果只需要赋值,比如修改某些数据,使用 setFieldsValue

另外值得注意的是 defaultValue 现在不能和 name 共存。详细可以看官网的说明

initialValue 虽然又被加了回来,但是优先级非常低,initialValues 仍然是优先级最高的,如果不是非常必要,尽量使用 initialValues。

validator 修改为 async

validator 原来一直使用 callback 来做异步,在 4 中,终于支持了更优雅的 async。

  1. <FormItem
  2. name="target"
  3. label="监控对象"
  4. rules={[
  5. {
  6. validator: async (_, value) => {
  7. if (value === '1') {
  8. return;
  9. }
  10. throw new Error('只允许选择表一哦');
  11. },
  12. },
  13. ]}
  14. />;

form 未绑定


如果在控制台中看到这个错误,一般是 Form.useForm() 的 生成的 form 没有使用。

image.png

form.validateFields() 无法获取值

这段代码其实是错误的

  1. const submit = () => {
  2. const msg = form.validateFields();
  3. request('url', {
  4. data: msg,
  5. });
  6. };

应该写这样 :

  1. const submit = async () => {
  2. try {
  3. const msg = await form.validateFields();
  4. request('url', {
  5. data: msg,
  6. });
  7. } catch (e) {
  8. console.log('我失败了', e);
  9. }
  10. };

很甜的语法糖

antd 中提供一些语法糖来方便做到我们原来需要自己实现的功能。

Form.List

这种列表在我们的项目中经常会遇到,原来我们需要写一堆代码。

image.png

  1. <Form.List name="names">
  2. {fields => (
  3. <div>
  4. {fields.map(field => (
  5. <Form.Item {...field}>
  6. <Input />
  7. </Form.Item>
  8. ))}
  9. </div>
  10. )}
  11. </Form.List>

多表单的互动

Form.Provider 提供了一个更简单的多表单互动方案,有效解决了 分布表单应该用一个表单还是多个表单的历史性难题。

  1. <Form.Provider
  2. onFormChange={()=>{
  3. // Do something...
  4. }}
  5. onFormFinish={name => {
  6. if (name === 'form1') {
  7. // Do something...
  8. }
  9. }}
  10. >
  11. <Form name="form1">...</Form>
  12. <Form name="form2">...</Form>
  13. </Form.Provider>

验证信息的模板

Form 的 validateMessages 支持了验证信息模板的功能,这个功能减少很多的工作量。

  1. const validateMessages = {
  2. required: "'${name}' 是必选字段",
  3. // ...
  4. };
  5. <Form validateMessages={validateMessages} />;

很多时候 name 是英文,这时候可以用

  1. const validateMessages = {
  2. required: "'${label}' 是必选字段",
  3. // ...
  4. };
  5. // 邪道用法,但是效果不错
  6. const validateMessages = {
  7. required: "此项为必填项",
  8. // ...
  9. };
  10. <Form validateMessages={validateMessages} />;

valuePropName=”fileList”

有些特殊的组件没有 value ,需要用 valuePropName 修改触发的值。

  1. <Form.Item name="switch" label="Switch" valuePropName="checked">
  2. <Switch />
  3. </Form.Item>
  4. <Form.Item
  5. name="upload"
  6. label="Upload"
  7. valuePropName="fileList"
  8. getValueFromEvent={normFile}
  9. >
  10. <Upload name="logo" action="/upload.do" listType="picture">
  11. <Button>
  12. <UploadOutlined /> Click to upload
  13. </Button>
  14. </Upload>
  15. </Form.Item>;