ant-design/components/form/v3.en-US.md
2020-02-11 17:04:13 +08:00

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" noStyle>
        <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>
  );
};

Replace validateFieldsAndScroll with scrollToField

New version recommend use onFinish for submit after validation. Thus validateFieldsAndScroll is change to more flexible method scrollToField:

// antd v3
onSubmit = () => {
  form.validateFieldsAndScroll((error, values) => {
    // Your logic
  });
};

To:

// antd v4
onFinishFailed = ({ errorFields }) => {
  form.scrollToField(errorFields[0].name);
};

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.

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
});