从 0 开始手写 rc-form(四)

9,079 阅读3分钟

「这是我参与2022首次更文挑战的第34天,活动详情查看:2022首次更文挑战」。

写在前面

  • 前面三篇文章,我们一步一步完善 createForm 的功能,逐步实现了用户数据输入的托管,输入数据的统一管理以及数据的校验
  • 前面也提到了,createForm 应该支持的功能有一下三点
    • 接管用户数据的输入,并统一管理输入的数据
    • 校验校验数据的正确性
    • 提交数据
  • 今天这篇文章,我们将继续完善 createForm 中的数据提交的功能

提交数据

  • 这里说的提交数据,指的就是从 createForm 所集中管理的数据中,获取数据
  • 其实也很简单,下面是代码实现
    getFieldsValue = () => {
      return this.state;
    };

    // 设置时就是传的一个对象
    setFieldsValue = (newValue) => {
      this.setState(newValue);
    };
  • getFieldsValue 就是获取数据的方法,直接返回 this.state 即可
  • 与 getFieldsValue 相对的是 setFieldsValue,用于设置数据
    • 到这里,可能有读者好奇了,不是已经托管了数据的输入吗,还要这个 setFieldsValue 干嘛
    • 有了 setFieldsValue,我们就可以通过代码去设置数据了,比如在组件挂载时,设置一个默认值
  • 写好了这两个方法,我们就需要将其暴露给外部,也就是添加到 getForm 方法返回的对象中
    getForm = () => ({
      getFieldDecorator: this.getFieldDecorator,
      getFieldsValue: this.getFieldsValue,
      setFieldsValue: this.setFieldsValue,
      validateFields: this.validateFields,
    });

    render() {
      const { ...otherProps } = this.props;
      const form = this.getForm();

      return <Com {...otherProps} form={form} />;
    }

验证 createForm 的可行性

  • 下面看下完整 demo 的代码
import React, { Component } from "react";
// import { createForm } from "rc-form";
import { createForm } from "./components/createForm";
import { Input } from "./components/Input";

const rules = {
  user: { required: true, message: "请输入用户名!" },
  pwd: { required: true, message: "请输入密码!" },
};

@createForm
export class MyCreateFormDemo extends Component {
  submit = () => {
    const { form } = this.props;
    const allValue = form.getFieldsValue();
    console.log(allValue);

    form.validateFields(({ err, value }) => {
      if (err) {
        console.log("error:", err);
      } else {
        console.log("success", value);
      }
    });
  };

  renderField = (type) => {
    const { form } = this.props;

    return form.getFieldDecorator(type, { rules: [rules[type]] })(
      <Input placeholder={type} />
    );
  };

  componentDidMount() {
    const { form } = this.props;
    form.setFieldsValue({ user: "defaultVal" });
  }

  render() {
    const { form } = this.props;
    console.log(form);

    return (
      <div>
        {this.renderField("user")}
        {this.renderField("pwd")}
        <button onClick={this.submit}>提交</button>
      </div>
    );
  }
}
  • 我们实现了一个 submit 方法,用于 MyCreateFormDemo 组件的数据提交
    • 在其内部,可以通过 form.getFieldsValue 获取所有输入到 MyCreateFormDemo 组件中的数据
    • 我们只需要调用 form.validateFields 进行数据校验,然后根据校验结果,执行相应操作即可
  • 下面是页面功能的运行情况:
  • 没有填写数据时,点击提交按钮

image.png

  • 填写一个数据时,点击提交按钮 image.png
  • 填写所有数据时,点击提交按钮 image.png

小结

  • 至此,我们的最简版本的 createForm 已经实现完成了
  • 怎么样,读者小伙伴们是不是感觉很 esay
  • 但是它的设计其实有个缺点,rc-form 是通过高阶组件来实现接管用户数据的输入,并统一管理输入的数据的
    • 也就意味着,所有的 数据输入组件 经过 getFieldDecorator 方法转换之后,都使用了 createForm 返回的组件的 state
    • 一旦有某一个 数据输入组件 开始接收数据,那么最上层的管理数据的组件的 state 就会发生改变,从而引起其进行更新
    • 由于我们自己的 form 组件通过高阶组件的转换之后,接收了来自父组件,也就是最上层管理数据的组件的 props,所以我们自己 form 组件的也会发生更新
    • 仅仅是某一个 数据输入组件 发生了更新,却会导致整个 form 组件都发生更新,这就造成了性能的浪费了
  • 因此,antd4 则重构了 form 组件,替换 rc-form 为 rc-field-form
  • rc-field-form 没有使用高阶组件,而是使用 hooks + contenxt 的方式,很好的解决了上面说的问题
  • 下篇文章,我们将继续手写 rc-field-form

最后

  • 今天的分享就到这里了,欢迎大家在评论区里面进行讨论 👏。
  • 如果觉得文章写的不错的话,希望大家不要吝惜点赞,大家的鼓励是我分享的最大动力 🥰