实现一个antd Form组件

515 阅读2分钟

FormStore类保存数据

  • 状态不保存在共同祖先form组件里,而是保存在第三方store里。原因是因为第一种情况当某个value更新时,会更新form的所有子组件。保存第三方store在value更新时找到对应的组件实例更新即可。
  • store保存表单数据,fieldEntities保存field组件实例,callbacks保存submit后回调方法
// 定义一个store
class FormStore {
  constructor() {
    this.store = {}; // 状态管理库 key: value
    this.fieldEntities = []; // field实例
    this.callbacks = {};
  }

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

  // 注册
  // 注册与取消注册 监听有取消监听 都是要成对出现的
  registerField = (field) => {
    this.fieldEntities.push(field);

    return () => {
      // 取消注册
      this.fieldEntities = this.fieldEntities.filter((_f) => _f !== field);
      delete this.store[field.props.name];
    };
  };

  // 操作状态管理库
  getFieldValue = (name) => {
    return this.store[name];
  };

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

  setFieldsValue = (newStore) => {
    // 1. update store
    this.store = {
      ...this.store,
      ...newStore,
    };
    // update Field
    this.fieldEntities.forEach((field) => {
      Object.keys(newStore).forEach((k) => {
        if (k === field.props.name) {
          field.onStoreChange();
        }
      });
    });
  };

  validate = () => {
    let err = [];
    // todo 检验
    return err;
  };

  submit = () => {
    let err = this.validate();
    if (err.length > 0) {
      // 校验失败  onFinishFailed
      this.callbacks.onFinishFailed(err, this.getFieldsValue());
    } else {
      // 校验通过 onFinish
      this.callbacks.onFinish(this.getFieldsValue());
    }
  };

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

form组件

  • 创建一个FormStore实列,使改组件有操作数据,校验数据,更新组件等的能力
  • 把FormStore实列通过context共享给子组件field
  • 用useRef保存 实例,保证组件更新不影响store
import React from "react";
import useForm from "./useForm";
import FieldContext from "./FieldContext";

// 2、通过Provider进行值的传递
export default function Form({children, form, onFinish, onFinishFailed}, ref) {
  const [formInstance] = useForm(form);

  React.useImperativeHandle(ref, () => formInstance);

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

field组件

  • 通过context获取form组件创建的FormStore实例,给子组件注入value和onchange等属性
  • 再渲染完成,准备销毁阶段通知store注册,销毁该组件实例
  • 声明onStoreChange更新该组件的方法,让获取field组件实例的store可以在值变更时更新该组件
import React, {Component} from "react";
import FieldContext from "./FieldContext";

// 3、子组件消费value
export default class Field extends Component {
  static contextType = FieldContext;

  componentDidMount() {
    // 注册field
    this.unregisterField = this.context.registerField(this);
  }

  // 取消注册
  componentWillUnmount() {
    this.unregisterField();
  }

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

  getControlled = () => {
    const {name} = this.props;
    return {
      value: this.context.getFieldValue(name), // getFieldValue(name)
      onChange: (e) => {
        const newValue = e.target.value;
        this.context.setFieldsValue({[name]: newValue});
      },
    };
  };
  render() {
    console.log("render"); //sy-log
    const returnChildNode = React.cloneElement(
      this.props.children,
      this.getControlled()
    );
    return returnChildNode;
  }
}