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