データスタックでのフォームの適用(パート2):詳細な記事

この記事のテーマは、データスタック製品で使用されたフォームフォームを理解して消化するプロセスです。フォームフォームで一般的に使用される方法をいくつか紹介することで、いくつかのデザインアイデアを理解し、テクノロジーの追求を深めることができます。主に、フォームフォームの作成とフォームフォームの双方向バインディング(getFieldDecorator)を紹介します。

後述するフォームフォームは、Antd 3.xのすべてのフォームコンポーネントであり、以降、フォームフォームと呼びます。データスタックでのフォームフォームの適用(パート1):検証で、私たちが最高の時代に生まれたと述べられています。実際、他の人が私たちが何かをするのを助けるために車輪を作ったので、今日見てみましょう、他の人のホイールの作り方、自分で実現できるでしょうか。Antdに注意を払った学生は、AntdがUIカプセル化用のreact-componentコンポーネントに基づいているという印象を持っているかもしれません。この記事では、react-component/formコードに焦点を当てます。

1.その他のフォーム

1.1 From.create

最初にcreateForm.jsファイルを確認してください。このファイルは主にcreateBaseForm.jsファイルいくつかの一般的なメソッドを追加します。

import createBaseForm from './createBaseForm';
​
export const mixin = {
  getForm() {
    return {
      getFieldsValue: this.fieldsStore.getFieldsValue,
      getFieldValue: this.fieldsStore.getFieldValue,
      ...
      validateFields: this.validateFields,
      resetFields: this.resetFields,
    };
  },
};
​
function createForm(options) {
  return createBaseForm(options, [mixin]);
}
​
export default createForm;

次に、createBaseForm.jsファイルを見てください。主にファイル内のcreateBaseFormメソッド。このメソッドはデコレーターとして機能し、デフォルトでフォームに設定されている変数を小道具でラップし、この変数でフォームのすべての機能を完了します。createBaseFormこの関数は、現在渡されているコンポーネントをコピーします。つまり、関数を呼び出して現在のコンポーネントをパッケージ化されたコンポーネントとして渡し、最後に新しいプロパティを持つパッケージ化されたコンポーネントを返します。

render() {
  const { wrappedComponentRef, ...restProps } = this.props; // eslint-disable-line
  const formProps = {
    // getForm 方法来自 createForm.js,在 props 中包装一个 formPropName 变量,默认为 form
    [formPropName]: this.getForm(),
  };
  // 获取 form 的实例
  if (withRef) {
    formProps.ref = 'wrappedComponent';
  } else if (wrappedComponentRef) {
    formProps.ref = wrappedComponentRef;
  }
  const props = mapProps.call(this, {
    ...formProps,
    ...restProps,
  });
  return <WrappedComponent {...props} />;
}

デコレータ:関連する構文であり、主にクラスとクラスメソッド(クラス属性)を変更するために使用されます。ほとんどのオブジェクト指向プログラミング言語は、Java、Pythonなどのこの構文をサポートしています。デコレータは、次のように簡単に理解できます。一部を对象変更して、ラップされたデコレータを返すことができ对象ます。

全体として、Form.create(options)実際にはビジネスコンポーネントを一度カプセル化し、Form関連のプロパティを初期化し、使用する必要のあるいくつかのメソッドをマウントし、これらのメソッドをprops.formに追加します。

1.2 getFieldDecorator

<FormItem {...formItemLayout} label="姓 名" >
  {getFieldDecorator('name', {
    initialValue: userInfo.name,
    rules: [
      { required: true, message: '姓名不可为空!' }
    ]
  })(
    <Input placeholder="请输入姓名" />
  )}
</FormItem>

上記のコードと以下の実装方法からgetFieldDecorator、カリー化された関数であることがわかります。idとパラメータの入力により、入力コンポーネントを入力として受け取り、新しいプロパティを追加するDomノードを返し、オプションのvaluePropName、getValueProps、initialValue。、ルール、およびその他の小道具が入力コンポーネントにマウントされます。

getFieldDecorator(name, fieldOption) {
  const props = this.getFieldProps(name, fieldOption);
  return fieldElem => {
    // We should put field in record if it is rendered
    this.renderFields[name] = true;
​
    const fieldMeta = this.fieldsStore.getFieldMeta(name);
    const originalProps = fieldElem.props;
    fieldMeta.originalProps = originalProps;
    fieldMeta.ref = fieldElem.ref;
    const decoratedFieldElem = React.cloneElement(fieldElem, {
      ...props,
      // 没有 initialValue 时为 undefined,有则是 initialValue 的值
      ...this.fieldsStore.getFieldValuePropValue(fieldMeta),
    });
    return supportRef(fieldElem) ? (
      decoratedFieldElem
    ) : (
      <FieldElemWrapper name={name} form={this}>
        {decoratedFieldElem}
      </FieldElemWrapper>
    );
  };
}

getFieldDecorator次の2つの関数createBaseForm.jsがあり、ファイルのgetFieldPropsメソッドとgetFieldValuePropValueメソッドで個別に検証できます。

  • データフィールドfieldsStoreを;
  • fieldsStoreデータフィールドは、小道具が入力コンポーネントに取り付けられているときに読み取られます。

1.3 validateFields

通常、このvalidateFieldsメソッドてフォームデータを検証します。createBaseForm.jsファイル内のvalidateFieldsメソッドの実装を確認した後、validateFieldsメソッドがPromiseを返し、validateFieldsInternalメソッドことがわかります。

validateFields(ns, opt, cb) {
  const pending = new Promise((resolve, reject) => {
    ...
    this.validateFieldsInternal(..., params, callback);
  });
  ...
  return pending;
}

validateFieldsInternalメソッドのコードを見ると、fieldsStoreからルールとデータフィールドの値が取得され、検証後に対応するエラー情報fieldsStoreが。

import AsyncValidator from 'async-validator';
​
validateFieldsInternal(
  fields,
  { fieldNames, action, options = {} },
  callback,
) {
  const fieldMeta = this.fieldsStore.getFieldMeta(name);
  ...
  const validator = new AsyncValidator(allRules);
  validator.validate(allValues, options, errors => {
    if (errors && errors.length) {
      errors.forEach(e => {
        ...
        const fieldErrors = get(errorsGroup, fieldName.concat('.errors'));
        fieldErrors.push(e);
      });
    }
  });
  ...
  this.setFields(nowAllFields);
  ...
}

一般に、フォームフォームは、初期化からフォームの収集と検証まで、次の手順を実行しました。1.Form.createメソッドprops.form開発者が呼び出すためにいくつかのプロパティが初期化されます。2.プロパティと値のgetFieldDecorator初期化てフォーム、双方向バインディングが実現されます。3.検証に合格した場合は、データfieldsStoreを;に保存します。検証が失敗した場合は、エラーを保存fieldsStoreしてレンダリングします。

2.あなた自身のフォーム

効果とコードはhttps://stackblitz.com/edit/react-ts-uoj5pjで見ることができますここに画像の説明を挿入

2.1 getFieldDecorator

/**
 * 实现 getFieldDecorator 方法
 * 初始化时将 initialValue 赋值给输入框的 value
 * 输入框变化时可以拿到 value
 */
const getFieldDecorator = (key: string, options: any) => {
  // 判断是否第一次赋值,避免死循环
  const first = Object.keys(formData).indexOf(key) === -1;
​
  if (options.rules) {
    rules[key] = [...options.rules];
  }
  if (first && options.initialValue) {
    setFormData({ ...formData, [key]: options.initialValue });
  }
​
  return (formItem) => {
    if (errObj[key]) {
      formItem = {
        ...formItem,
        props: { ...formItem.props, className: 'input err' },
      };
    }
    return (
      <div className="form-item">
        {React.cloneElement(formItem, {
          name: key,
          value: formData[key] || '',
          onChange: (e: any) => {
            // 输入框值变化时去除错误提示
            setErrObj({ ...errObj, [key]: '' });
            setFormData({ ...formData, [key]: e.target.value });
          },
          onBlur: () => {
            // 当前默认 blur 时进行校验
            validateFields();
          },
        })}
        <div className="err-text">{errObj[key] || ' '}</div>
      </div>
    );
  };
};

2.2 validateFields

// 绑定校验方法
const validateFields = (cb?: any) => {
  let errObjTemp = {};
  Object.keys(rules).forEach((key) => {
    rules[key].forEach((rule) => {
      if (rule?.required && (!formData[key] || formData[key].trim() === '')) {
        errObjTemp = {
          ...errObjTemp,
          [key]: rule?.message || `${key}为必填项!`,
        };
        setErrObj(errObjTemp);
      }
    });
  });
  cb && cb(Object.keys(errObjTemp).length ? errObjTemp : undefined, formData);
};

2.3 createForm

const createForm = (FormFunc) => (props) => {
  const [formData, setFormData] = useState({});
  const [errObj, setErrObj] = useState({});
  const rules = {};
  
  ...
  
  // 将自定义方法挂载到 props 上
  return FormFunc({ ...props, getFieldDecorator, validateFields });
};
{{o.name}}
{{m.name}}

おすすめ

転載: my.oschina.net/u/3869098/blog/5403594