O tema deste artigo é um processo de compreensão e digestão de formulários de formulários após serem usados em produtos de pilha de dados.Ao introduzir alguns métodos comumente usados em formulários de formulários, podemos entender algumas ideias de design e aprofundar nossa busca por tecnologia. Ele apresenta principalmente a criação do formulário Form e a ligação bidirecional do formulário Form (getFieldDecorator).
Os formulários de formulário mencionados posteriormente são todos componentes de formulário no Antd 3.x, doravante denominados formulários de formulário. Na aplicação do formulário Form na pilha de dados (Parte 1): Verificação, é mencionado que nascemos na melhor era. Na verdade, outros construíram as rodas para nos ajudar a fazer algumas coisas, então vamos dar uma olhada hoje, de outras pessoas Como a roda é feita, podemos perceber isso nós mesmos. Os alunos que prestaram atenção ao Antd podem ter a impressão de que o Antd é baseado em componentes react-component para encapsulamento de interface do usuário, e o artigo se concentrará no código react-component/form.
1. Formulário de outros
1.1 De.criar
Verifique primeiro o arquivo createForm.js. Esse arquivo encapsula principalmente o createBaseForm.js
arquivo e adiciona alguns métodos comuns.
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;
Em seguida, dê uma olhada no arquivo createBaseForm.js, principalmente veja o createBaseForm
método , este método atua como um decorador, encapsula uma variável que tem como padrão o form em props, e completa todas as funções do Form nesta variável. createBaseForm
A função é copiar o componente atualmente passado, ou seja, chamar a função para passar o componente atual como o componente empacotado e, finalmente, retornar um componente empacotado com novas propriedades.
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} />;
}
Decorator: é uma sintaxe
类
relacionada , usada principalmente para modificar classes e métodos de classe (atributos de classe), a maioria das linguagens de programação orientadas a objetos suportam essa sintaxe, como Java, Python. Um decorador pode ser entendido simplesmente como: ele对象
pode e retornar um encapsulado对象
.
Em geral, Form.create(options)
nós realmente encapsulamos nossos componentes de negócios uma vez, inicializamos propriedades relacionadas a Form, montamos alguns métodos que precisam ser usados e adicionamos esses métodos a props.form.
1.2 getFieldDecorator
<FormItem {...formItemLayout} label="姓 名" >
{getFieldDecorator('name', {
initialValue: userInfo.name,
rules: [
{ required: true, message: '姓名不可为空!' }
]
})(
<Input placeholder="请输入姓名" />
)}
</FormItem>
Pode-se ver no código acima e no método de implementação abaixo que getFieldDecorator
é uma função curry. Através da entrada de id e parâmetros, ela retorna um nó Dom que recebe o componente de entrada como entrada e adiciona uma nova propriedade, e define o valuePropName, getValueProps, initialValue da opção. , regras e outras props são montadas no componente de entrada.
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
Ele possui as duas funções a seguir, que podem createBaseForm.js
ser verificadas separadamente nos métodosgetFieldProps
e do arquivo:getFieldValuePropValue
- Coloque o campo
fieldsStore
de ; - Os campos de dados
fieldsStore
são .
1.3 validar Campos
Normalmente usamos o validateFields
método para verificar os dados do nosso formulário.Após visualizar a implementação do métodocreateBaseForm.js
no arquivo , descobrimos que o método retorna um Promise e monta os parâmetros exigidos pelo método .validateFields
validateFields
validateFieldsInternal
validateFields(ns, opt, cb) {
const pending = new Promise((resolve, reject) => {
...
this.validateFieldsInternal(..., params, callback);
});
...
return pending;
}
Observe o código do validateFieldsInternal
método , ele fieldsStore
obterá os valores das regras e campos de dados de, e armazenará as informações do erro fieldsStore
no .
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);
...
}
Em geral, o formulário Form passou pelos seguintes passos desde a inicialização até a coleta e verificação do formulário: 1. Através do Form.create
método algumas propriedades são inicializadas props.form
para que os desenvolvedores chamem; 2. Através da getFieldDecorator
inicialização das propriedades e valores do formulário formulário, a ligação bidirecional é alcançada 3. Se a verificação for aprovada, salve os dados fieldsStore
em , se a verificação falhar, salve o erro fieldsStore
e renderize.
2. Seu próprio formulário
O efeito e o código podem ser vistos em 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 validar Campos
// 绑定校验方法
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 criarFormulário
const createForm = (FormFunc) => (props) => {
const [formData, setFormData] = useState({});
const [errObj, setErrObj] = useState({});
const rules = {};
...
// 将自定义方法挂载到 props 上
return FormFunc({ ...props, getFieldDecorator, validateFields });
};