antd4使用form表单

antd3怎么用都很熟了。
但是antd4表单组件换了很多新东西,而且还是用hook或者ts去实现的。感觉看了一下文档脑子都乱了,因此整理一下记在这里。

会有一些关于3.x版本和4.x版本的Form使用对比。
把很多原本的函数实现,Form.create生成的表单实例东西都给放进了组件属性里。
这意味着啥呢。。
你再也不用用getFieldDecorator去包裹form组件了,可以直接在formItem里对表单填写进行操作。

先看看antd里怎么写form的API吧
https://ant.design/components/form-cn/

被设置了 name 属性的 Form.Item 包装的控件,表单控件会自动添加 value(或 valuePropName 指定的其他属性) onChange(或 trigger 指定的其他属性),数据同步将被 Form 接管

1、最普通的用法

现在最普通的用法,不用Form.create()注入,也可以使用方便快捷的表单函数了,做简单的表单校验没有问题,一些快捷的表单使用比3.x版本的方便。
我尝试用Hook+typescript的方式写
antd4使用form表单_第1张图片

import React, {
      useEffect, useState } from 'react';
import {
      Input, Form, Button } from 'antd';
import {
      ColumnProps } from 'antd/es/table';

interface iformProps {
     
  /** 回调函数,传回form表单填写的text数据 */
  onSubmit: (value: string) => void;
}

export default function FormFC(props: iformProps): JSX.Element {
     
  return (
    <>
      <Form
        name="basic"
        /** 默认初始数据全部在Form里注入了,不用包装函数 */
        initialValues={
     {
      text: '初始数据' }}
        /** 提交校验也不在button的onClick里,onFinish是成功的回调,onFinishFailed是失败的回调,注意Button要写在Form里,并且htmlType="submit" */
        onFinish={
     (value) => console.log(value)}
        onFinishFailed={
     (value) => console.log(value)}
        /** 不用提交,每一次数据变化都把值返回上层组件 */
        onFieldsChange={
     (changedFields, allFields) => console.log('onFieldsChange', changedFields, allFields)}
        onValuesChange={
     (changedFields, allFields) => console.log('onValuesChange', changedFields, allFields)}
        style={
     {
      display: 'flex' }}
      >
        <Form.Item
          /** 以前写在包装函数里的东西全部作为formItem的属性了 */
          label="要做的事情"
          name="text"
          rules={
     [{
      required: true, message: '今天你要做的事还没写' }]}
          style={
     {
      flex: 1, paddingRight: '10px' }}
        >
          <Input />
        </Form.Item>
        {
     /*  */}
      </Form>
    </>
  );
}

但是这样使用有几个问题:
1、如果button不放在Form里面,怎么提交表单呢?(不过放在Form里,可以用FormItem包裹button进行布局)
目前我的研究是:如果button不放在Form里以及htmlType没有设置"submit",就不能点击提交表单,调用封装在组件里的验证函数。(但是3.x版本注入到props里是可以调用的,这又是一点不同)
如果非得要放在外面,还要调用submit函数,那只能获取Form的实例来用了,相当于底下的useForm或者ref获取。这样还不如直接用底下的方法二、方法三这样。

2、如果想要重置表单呢?
回答同上,只能调用form的实例来用。

3、如果作为一个类似于卡片的组件,不需要提交(不需要按钮),只需要把数据反馈给上层组件,有没有办法呢?
就使用上面例子中的onFieldsChange和 onValuesChange,普遍来说还是用onValuesChange吧,但是onValuesChange不携带表单是否错误的消息,只是单纯的表单域值返回。

但是我使用的时候发现一个情况,每一次onChange修改,调用1次onValuesChange函数,却调用了三次onFieldsChange函数。
antd4使用form表单_第2张图片
antd文档解释如下:
antd4使用form表单_第3张图片

2、Form.useForm()

官方推荐使用 Form.useForm 创建表单数据域进行控制。如果是在 class component 下,应该通过 ref 获取数据域。

hook的使用方法,会自动创建一个form实例。
const [form] = Form.useForm();
把form打印出来看到是一个object,打印出来如下:和官方给的一模一样
antd4使用form表单_第4张图片

form实例拥有的API如下,几乎就是form.create()创建出来的实例一样
antd4使用form表单_第5张图片

官方案例在这里:
antd4使用form表单_第6张图片


useForm就普通用法就好,把它当成v3.x里的props.form就行,里面的函数会有一点不同,但只是在方法一的基础上改了一点点,看懂了方法一,使用useForm就会习惯了。

如果方便button还是放在< Form>里会方便一点,设置htmlType=‘submit’,之后Form记得设置onFinish就可以了。

如果button不放在表单里,就设置一下onClick,调用form.submit()或者form.validateFields()都可以,使用例子在下面。

消化之后的练习Demo心得:

import React, {
      useEffect, useState } from 'react';
import {
      Input, Form, Button } from 'antd';

export default function FormFC<T>(props: T): JSX.Element {
     
  /** 实例化Form */
  const [form] = Form.useForm();

  /** 表单里的onFinish */
  const onFinish = <T extends {
     }>(values: T): void => {
     
    console.log(values);
  };

  /** 表单里的onFinishFailed */
  const onFinishFailed = <T extends {
     }>(err: T): void => {
     
    console.log(err);
  };

  /** 点击提交按钮的submit,尝试不要把submit写在Form表单里的用法 */
  const onSubmit = (): void => {
     
    /** 调用这个提交,会自动调用onFinsh和onFinishFailed */
    // form.submit();

    /** 尝试使用validate,v3.x版本的都知道我为啥要调这个函数的。。。用习惯了
     * 但是v4.0这个函数返回的是一个promise,而且参数类型是NamePath(看官网,fieldNames 数组属性,用来自定义哪些字段需要校验收集)
     * 以下写法参照官方给出的教程。
     */
    form
      .validateFields()
      .then((values) => {
     
        /** 正确后的验证信息 */
        console.log(values);
      })
      .catch((errorInfo) => {
     
        /** 错误信息 */
        console.log(errorInfo);
      });
  };

  /**
   * 非箭头函数使用泛型
   * function test(value: T): T{
   *   return value
   * }
   */
  const onReset = () => {
     
    form.resetFields();
  };

  const onFill = () => {
     
    form.setFieldsValue({
     
      text: '哈哈哈哈哈',
    });
  };
  return (
    <>
      <Form form={
     form} name="formFC" initialValues={
     {
      text: '初始数据' }} onFinish={
     onFinish} onFinishFailed={
     onFinishFailed}>
        <Form.Item name="text" label="要做的事" rules={
     [{
      required: true }]}>
          <Input />
        </Form.Item>
      </Form>
      {
     /* 如果要用e,则onClick={(e: React.MouseEvent)=>onSubmit()} */}
      <Button onClick={
     onSubmit}>提交</Button>
      <Button onClick={
     onReset}>重置</Button>
      <Button onClick={
     onFill}>设置表单值</Button>
    </>
  );
}

3、在Component里使用

几乎和上面没啥区别吧。懒得再多写一个了。看官方例子就好。

------------------------------------------

------------------------------------------

思考一个问题:如果一个页面有两个表单呢,基本上还是会遇到这样的情况的,一个页面多个表单
antd4使用form表单_第7张图片
测试一下v4.0有没有更好的校验办法,v3.0这个问题可真是有点头疼。
就用上面的例子,再多加一个表单。

按钮在Form组件外的时候,点击提交onSubmit,输出的form实例包含了整个页面的表单数据。
antd4使用form表单_第8张图片

把button挪到表单里面,效果同样,输出了整个页面的表单数据。
antd4使用form表单_第9张图片
看来这点还是和3.0版本略像啊。。。一直没搞懂Form的name属性是拿来干嘛的。
提供几点解决办法:
1、把两个表单放在两个函数组件里,然后创建form实例,单独校验,就没事了。

2、比如某个表单显示,某个表单隐藏的情况,肯定有一个visible字段判断是那个表单在用是吧,那就把FormItem的校验规则rule里面的required选项设置成动态的。比如required:visible。

3、使用校验validate函数里传入namePath,判断校验哪个字段,对于字段不多的表单还是挺好用的。

4、听说可以用ref来创建一个组件里的不同form实例。(实验后表示不行,一个组件里也许只能有一个实例。
antd4使用form表单_第10张图片

——————————————————

知道name 有啥用了。在Form.Provider里可以根据name判断当前提交表单。
Form.Provider:提供表单间联动功能,其下设置 name 的 Form 更新时,会自动触发对应事件。
antd4使用form表单_第11张图片
antd4使用form表单_第12张图片
antd4使用form表单_第13张图片
antd4使用form表单_第14张图片




怎么在Form.Provider里对单独的某一个表单提交?
试了试。。。。感觉弄不好就卡死了啊,还不知道为啥卡死了。
感觉不咋好用啊这个Provider。如果需要多表单校验,我估计可以搞一个useContext传form实例过去,这样大家都在一个实例里了,具体的如果遇到类似问题再说吧。
antd4使用form表单_第15张图片

你可能感兴趣的:(前端)