antd4 Form表单源码学习

124 阅读2分钟

Form表单思维导图

image.png

demo

import React, { Component, useEffect } from "react";
// import Form, { Field } from "rc-field-form";
import Form, { Field } from "../components/my-rc-field-form/";
import Input from "../components/Input";

const nameRules = { required: true, message: "请输入姓名!" };
const passworRules = { required: true, message: "请输入密码!" };

// export default function MyRCFieldForm(props) {
//   const [form] = Form.useForm();

//   const onFinish = (val) => {
//     console.log("onFinish", val); //sy-log
//   };

//   // 表单校验失败执行
//   const onFinishFailed = (val) => {
//     console.log("onFinishFailed", val); //sy-log
//   };

//   useEffect(() => {
//     console.log("form", form); //sy-log
//     // form.setFieldsValue({ username: "default" });
//   }, []);

//   return (
//     <div>
//       <h3>MyRCFieldForm</h3>
//       <Form form={form} onFinish={onFinish} onFinishFailed={onFinishFailed}>
//         <Field name="username" rules={[nameRules]}>
//           <Input placeholder="input UR Username" />
//         </Field>
//         <Field name="password" rules={[passworRules]}>
//           <Input placeholder="input UR Password" />
//         </Field>
//         <button>Submit</button>
//       </Form>
//     </div>
//   );
// }

export default class MyRCFieldForm extends Component {
  formRef = React.createRef();
  componentDidMount() {
    console.log("form", this.formRef.current); //sy-log
    this.formRef.current.setFieldsValue({ username: "default" });
  }

  onFinish = (val) => {
    console.log("onFinish", val); //sy-log
  };

  // 表单校验失败执行
  onFinishFailed = (val) => {
    console.log("onFinishFailed", val); //sy-log
  };
  render() {
    return (
      <div>
        <h3>MyRCFieldForm</h3>
        <Form
          ref={this.formRef}
          onFinish={this.onFinish}
          onFinishFailed={this.onFinishFailed}
        >
          <Field name="username" rules={[nameRules]}>
            <Input placeholder="Username" />
          </Field>
          <Field name="password" rules={[passworRules]}>
            <Input placeholder="Password" />
          </Field>
          <button>Submit</button>
        </Form>
      </div>
    );
  }
}

入口

import React from "react";
import _Form from "./Form";
import Field from "./Field";
import useForm from "./useForm";

const Form = React.forwardRef(_Form); //_Form;

Form.Field = Field;
Form.useForm = useForm;

export { Field, useForm };
export default Form;

Form组件

import React from "react";
import FieldContext from "./FieldContext";
import useForm from "./useForm";

//ref传给子组件的时候不在props里,会独立出来
export default function Form(
  { children, form, onFinish, onFinishFailed },
  ref
) {
  const [formInstance] = useForm(form);
  //useImperativeHandle:将formInstance反弹给父组件使用
  React.useImperativeHandle(ref, () => formInstance);

  formInstance.setCallbacks({
    onFinish,
    onFinishFailed,
  });
  return (
    <form
      onSubmit={(e) => {
        e.preventDefault();
        formInstance.submit();
      }}
    >
      <FieldContext.Provider value={formInstance}>
        {children}
      </FieldContext.Provider>
    </form>
  );
}

  • ref传给子组件的时候不在props里,会独立出来
  • useImperativeHandle:将formInstance反弹给父组件使用(getFieldValue、setFieldsValue等方法)

Field组件

import React, { Component } from "react";
import FieldContext from "./FieldContext";

export default class Field extends Component {
  static contextType = FieldContext;

  componentDidMount() {
    this.unregister = this.context.registerFieldEntities(this);
  }

  componentWillUnmount() {
    this.unregister();
  }

  onStoreChange = () => {
    this.forceUpdate();
  };

  getControlled = () => {
    const { getFieldValue, setFieldsValue } = this.context;
    const { name } = this.props;
    return {
      value: getFieldValue(name), //"omg", // get state
      onChange: (e) => {
        const newValue = e.target.value;
        // set state
        setFieldsValue({ [name]: newValue });
      },
    };
  };
  render() {
    const { children } = this.props;

    const returnChildNode = React.cloneElement(children, this.getControlled());
    return returnChildNode;
  }
}

// export default function Field(props) {
//   const { children, name } = props;

//   const { getFieldValue, setFieldsValue, registerFieldEntities } =
//     React.useContext(FieldContext);

//   const [, forceUpdate] = React.useReducer((x) => x + 1, 0);
//   //这里不能用useEffect,useEffect会延时执行注册,componentDidMount里的更新会被忽略掉,只能用useLayoutEffect注册
//   React.useLayoutEffect(() => {
//     const unregister = registerFieldEntities({
//       props,
//       onStoreChange: forceUpdate,
//     });
//     return unregister;
//   }, []);

//   const getControlled = () => {
//     return {
//       value: getFieldValue(name), //"omg", // get state
//       onChange: (e) => {
//         const newValue = e.target.value;
//         // set state
//         setFieldsValue({ [name]: newValue });
//       },
//     };
//   };

//   const returnChildNode = React.cloneElement(children, getControlled());
//   return returnChildNode;
// }

  • 这里不能用useEffect,useEffect会延时执行注册,componentDidMount里的更新会被忽略掉,只能用useLayoutEffect注册

FormStore和useForm

// 定义状态管理库

import { useRef } from "react";

class FormStore {
  constructor() {
    this.store = {}; // 状态值: name: value
    this.fieldEntities = [];

    this.callbacks = {};
  }

  setCallbacks = (callbacks) => {
    this.callbacks = { ...this.callbacks, ...callbacks };
  };

  // 注册实例(forceUpdate)
  // 注册与取消注册
  // 订阅与取消订阅
  registerFieldEntities = (entity) => {
    this.fieldEntities.push(entity);

    return () => {
      this.fieldEntities = this.fieldEntities.filter((item) => item !== entity);
      delete this.store[entity.props.name];
    };
  };

  // get
  getFieldsValue = () => {
    return { ...this.store };
  };

  getFieldValue = (name) => {
    return this.store[name];
  };

  // set
  // password: 123
  setFieldsValue = (newStore) => {
    // 1. update store
    this.store = {
      ...this.store,
      ...newStore,
    };
    // 2. update Field 数据更新之后组件也得更新
    this.fieldEntities.forEach((entity) => {
      Object.keys(newStore).forEach((k) => {
        if (k === entity.props.name) {
          entity.onStoreChange();
        }
      });
    });
  };

  validate = () => {
    let err = [];
    // todo 校验
    // 简版校验

    this.fieldEntities.forEach((entity) => {
      const { name, rules } = entity.props;

      const value = this.getFieldValue(name);
      let rule = rules[0];

      if (rule && rule.required && (value === undefined || value === "")) {
        err.push({ [name]: rule.message, value });
      }
    });

    return err;
  };

  submit = () => {
    console.log("submit"); //sy-log

    let err = this.validate();
    // 提交
    const { onFinish, onFinishFailed } = this.callbacks;

    if (err.length === 0) {
      // 校验通过
      onFinish(this.getFieldsValue());
    } else {
      // 校验不通过
      onFinishFailed(err, this.getFieldsValue());
    }
  };

  getForm = () => {
    return {
      getFieldsValue: this.getFieldsValue,
      getFieldValue: this.getFieldValue,
      setFieldsValue: this.setFieldsValue,
      registerFieldEntities: this.registerFieldEntities,
      submit: this.submit,
      setCallbacks: this.setCallbacks,
    };
  };
}

export default function useForm(form) {
  // 存值,在组件卸载之前指向的都是同一个值
  const formRef = useRef();

  if (!formRef.current) {
    if (form) {
      formRef.current = form;
    } else {
      const formStore = new FormStore();
      formRef.current = formStore.getForm();
    }
  }
  return [formRef.current];
}

  • 关注store、fieldEntities和callBacks的变更以及获取
  • 每次setFieldsValue之后应该从注册里获取Field组件强制更新的方法并执行
  • 区别函数式组件和类组件使用context的区别

FieldContext

import React from "react";

const FieldContext = React.createContext();

export default FieldContext;