Aplicação do formulário na pilha de dados (parte 2): artigos aprofundados

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.jsarquivo 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 createBaseFormmé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. createBaseFormA 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>
    );
  };
}

getFieldDecoratorEle possui as duas funções a seguir, que podem createBaseForm.jsser verificadas separadamente nos métodosgetFieldProps e do arquivo:getFieldValuePropValue

  • Coloque o campo fieldsStorede ;
  • Os campos de dados fieldsStoresão .

1.3 validar Campos

Normalmente usamos o validateFieldsmé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 .validateFieldsvalidateFieldsvalidateFieldsInternal

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

Observe o código do validateFieldsInternalmétodo , ele fieldsStoreobterá os valores das regras e campos de dados de, e armazenará as informações do erro fieldsStoreno .

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.createmétodo algumas propriedades são inicializadas props.formpara que os desenvolvedores chamem; 2. Através da getFieldDecoratorinicializaçã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 fieldsStoreem , se a verificação falhar, salve o erro fieldsStoree renderize.

2. Seu próprio formulário

O efeito e o código podem ser vistos em https://stackblitz.com/edit/react-ts-uoj5pj .insira a descrição da imagem aqui

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 });
};
{{o.name}}
{{m.name}}

Acho que você gosta

Origin my.oschina.net/u/3869098/blog/5403594
Recomendado
Clasificación