Maison  >  Article  >  interface Web  >  Explication détaillée de l'implémentation de l'encapsulation des composants React Form

Explication détaillée de l'implémentation de l'encapsulation des composants React Form

php中世界最好的语言
php中世界最好的语言original
2018-05-31 10:28:061308parcourir

Cette fois, je vais vous apporter une explication détaillée de la mise en œuvre de l'encapsulation des composants React Form. Quelles sont les précautions pour la mise en œuvre de l'encapsulation des composants React Form. Ce qui suit est un cas pratique, prenons un. regarder.

Avant-propos

Pour les systèmes Web, la soumission de formulaires est un moyen très courant d'interagir avec les utilisateurs, par exemple lors de la soumission d'une commande, vous. Vous devez saisir le destinataire, le numéro de téléphone portable, l'adresse et d'autres informations, ou lors de la configuration du système, vous devez renseigner certaines informations de préférences personnelles. La soumission de formulaire est une opération structurée qui peut simplifier le développement en encapsulant certaines fonctions courantes. Cet article discutera des idées de conception du composant Formform et présentera la méthode d'implémentation spécifique en conjonction avec le composant ZentForm. Le code impliqué dans cet article est basé sur React v15.

Fonctions du composant Form

De manière générale, les fonctions du composant Form incluent les points suivants :

  1. Mise en page du formulaire

  2. Champs du formulaire

  3. EncapsulationValidation du formulaire& Invite d'erreur

  4. Soumission du formulaire

La mise en œuvre de chaque partie sera présentée en détail ci-dessous.

Mise en page des formulaires

Il existe généralement 3 façons de mettre en page les formulaires couramment utilisées :

Mise en page en ligne

Mise en page horizontale

Mise en page verticale

La méthode d'implémentation est relativement simple, il suffit d'imbriquer les CSS. Par exemple, la structure du formulaire est la suivante :

<form class="form">
  <label class="label"/>
  <field class="field"/>
</form>

correspond à 3 mises en page Il vous suffit d'ajouter la classe correspondante à la balise form :

<!--行内布局-->
<form class="form inline">
  <label class="label"/>
  <field class="field"/>
</form>
<!--水平布局-->
<form class="form horizontal">
  <label class="label"/>
  <field class="field"/>
</form>
<!--垂直布局-->
<form class="form vertical">
  <label class="label"/>
  <field class="field"/>
</form>

En conséquence, vous devez définir le CSS des 3 mises en page :

.inline .label {
  display: inline-block;
  ...
}
.inline .field {
  display: inline-block;
  ...
}
.horizontal .label {
  display: inline-block;
  ...
}
.horizontal .field {
  display: inline-block;
  ...
}
.vertical .label {
  display: block;
  ...
}
.vertical .field {
  display: block;
  ...
}

Encapsulation du champ du formulaire

La partie encapsulation du champ est généralement une couche. d'encapsulation des composants de la bibliothèque de composants du Form, tels que le composant Input, le composant Select, le composant Checkbox, etc. Lorsque les champs existants ne peuvent pas répondre à vos besoins, vous pouvez personnaliser les champs.

Les champs du formulaire comprennent généralement deux parties, l'une est le titre et l'autre est le contenu. ZentForm encapsule la structure et le style via la fonction d'ordre supérieur getControlGroup. Son paramètre d'entrée est le composant à afficher :

export default Control => {
  render() {
    return (
      <p className={groupClassName}>
        <label className="zent-formcontrol-label">
          {required ? <em className="zent-formrequired">*</em> : null}
          {label}
        </label>
        <p className="zent-formcontrols">
          <Control {...props} {...controlRef} />
          {showError && (
            <p className="zent-formerror-desc">{props.error}</p>
          )}
          {notice && <p className="zent-formnotice-desc">{notice}</p>}
          {helpDesc && <p className="zent-formhelp-desc">{helpDesc}</p>}
        </p>
      </p>
     );                          
  }
}

Les informations d'étiquette et d'erreur utilisées ici proviennent du composant Field Incoming :

<Field
  label="预约门店:"
  name="dept"
  component={CustomizedComp}
  validations={{
    required: true,
  }}
  validationErrors={{
    required: &#39;预约门店不能为空&#39;,
  }}
  required
/>

Le CustomizedComp ici est le composant renvoyé après avoir été encapsulé par getControlGroup.

L'interaction entre les champs et les formulaires est un problème qui doit être pris en compte. Le formulaire doit connaître les valeurs des champs qu'il contient et doit vérifier les champs au moment approprié. L'implémentation de ZentForm consiste à conserver un tableau de champs dans le composant d'ordre supérieur du formulaire, et le contenu du tableau est une instance de Field. Par la suite, l’objectif d’acquisition et de vérification de la valeur est atteint en exploitant ces instances.

ZentForm est utilisé comme suit :

class FieldForm extends React.Component {
  render() {
    return (
      <Form>
        <Field
          name="name"
          component={CustomizedComp}
      </Form>
    )
  }
}
export default createForm()(FieldForm);

Où Form et Field sont des composants fournis par la bibliothèque de composants, CustomizedComp est un composant personnalisé et createForm est une fonction d'ordre supérieur fournie par le bibliothèque de composants. Dans le composant renvoyé par createForm, un tableau de champs est conservé et deux méthodes, attachToForm et detachFromForm, sont fournies pour faire fonctionner ce tableau. Ces deux méthodes sont enregistrées dans l'objet contextuel et Field peut être appelé lors du chargement et du déchargement. Le code simplifié est le suivant :

/**
 * createForm高阶函数
 */
const createForm = (config = {}) => {
  ...
  return WrappedForm => {
    return class Form extends Component {
      constructor(props) {
        super(props);
        this.fields = [];
      }
      
      getChildContext() {
        return {
          zentForm: {
            attachToForm: this.attachToForm,
            detachFromForm: this.detachFromForm,
          }
        }
      }
      
      attachToForm = field => {
        if (this.fields.indexOf(field) < 0) {
          this.fields.push(field);
        }
      };
    
      detachFromForm = field => {
        const fieldPos = this.fields.indexOf(field);
        if (fieldPos >= 0) {
          this.fields.splice(fieldPos, 1);
        }
      };
      
      render() {
        return createElement(WrappedForm, {...});
      }
    } 
  }
}
/**
 * Field组件
 */
class Field extends Component {
  componentWillMount() {
    this.context.zentForm.attachToForm(this);
  }
  
  componentWillUnmount() {
    this.context.zentForm.detachFromForm(this);
  }
  
  render() {
    const { component } = this.props;
    return createElement(component, {...});
  }
}

Lorsque vous avez besoin d'obtenir la valeur du champ du formulaire, il vous suffit de parcourir le tableau des champs puis d'appeler la méthode correspondante de l'instance Field :

/**
 * createForm高阶函数
 */
const createForm = (config = {}) => {
  ...
  return WrappedForm => {
    return class Form extends Component {
      getFormValues = () => {
        return this.fields.reduce((values, field) => {
          const name = field.getName();
          const fieldValue = field.getValue();
          values[name] = fieldValue;
          return values;
        }, {});
       };
    } 
  }
}
/**
 * Field组件
 */
class Field extends Component {
  getValue = () => {
    return this.state._value;
  };
}

Validation du formulaire et invite d'erreur

表单验证是一个重头戏,只有验证通过了才能提交表单。验证的时机也有多种,如字段变更时、鼠标移出时和表单提交时。ZentForm提供了一些常用的验证规则,如非空验证,长度验证,邮箱地址验证等。当然还能自定义一些更复杂的验证方式。自定义验证方法可以通过两种方式传入ZentForm,一种是通过给createForm传参:

createForm({
  formValidations: {
    rule1(values, value){
    },
    rule2(values, value){
    },
  }
})(FormComp);

另一种方式是给Field组件传属性:

<Field
  validations={{
    rule1(values, value){
    },
    rule2(values, value){
    },
  }}
  validationErrors={{
    rule1: &#39;error1&#39;,
    rule2: &#39;error2&#39;
  }}
/>

使用createForm传参的方式,验证规则是共享的,而Field的属性传参是字段专用的。validationErrors指定校验失败后的提示信息。这里的错误信息会显示在前面getControlGroup所定义HTML中{showError && (<p className="zent-formerror-desc">{props.error}</p>)}

ZentForm的核心验证逻辑是createForm的runRules方法,

runRules = (value, currentValues, validations = {}) => {
  const results = {
    errors: [],
    failed: [],
  };
  function updateResults(validation, validationMethod) {
    // validation方法可以直接返回错误信息,否则需要返回布尔值表明校验是否成功
    if (typeof validation === 'string') {
      results.errors.push(validation);
      results.failed.push(validationMethod);
    } else if (!validation) {
      results.failed.push(validationMethod);
    }
  }
  Object.keys(validations).forEach(validationMethod => {
    ...
    // 使用自定义校验方法或内置校验方法(可以按需添加)
    if (typeof validations[validationMethod] === 'function') {
      const validation = validations[validationMethod](
        currentValues,
        value
      );
      updateResults(validation, validationMethod);
    } else {
      const validation = validationRules[validationMethod](
        currentValues,
        value,
        validations[validationMethod]
      );
    }
  });
  
  return results;
};

默认的校验时机是字段值改变的时候,可以通过Field的validate<a href="http://www.php.cn/wiki/1464.html" target="_blank">OnChange</a>validateOnBlur来改变校验时机。

<Field
  validateOnChange={false}
  validateOnBlur={false}
  validations={{
    required: true,
    matchRegex: /^[a-zA-Z]+$/
  }}
  validationErrors={{
    required: &#39;值不能为空&#39;,
    matchRegex: &#39;只能为字母&#39;
 }}
/>

对应的,在Field组件中有2个方法来处理change和blur事件:

class Field extends Component {
  handleChange = (event, options = { merge: false }) => {
    ...
    this.setValue(newValue, validateOnChange);
    ...
  }
  
  handleBlur = (event, options = { merge: false }) => {
    ...
    this.setValue(newValue, validateOnBlur);
    ...
  }
  
  setValue = (value, needValidate = true) => {
    this.setState(
      {
        _value: value,
        _isDirty: true,
      },
      () => {
        needValidate && this.context.zentForm.validate(this);
      }
    );
 };
}

当触发验证的时候,ZentForm是会对表单对所有字段进行验证,可以通过指定relatedFields来告诉表单哪些字段需要同步进行验证。

表单提交

表单提交时,一般会经历如下几个步骤

  1. 表单验证

  2. 表单提交

  3. 提交成功处理

  4. 提交失败处理

ZentForm通过handleSubmit高阶函数定义了上述几个步骤,只需要传入表单提交的逻辑即可:

const handleSubmit = (submit, zentForm) => {
  const doSubmit = () => {
    ...
    result = submit(values, zentForm);
    ...  
    return result.then(
      submitResult => {
        ...
        if (onSubmitSuccess) {
          handleOnSubmitSuccess(submitResult);
        }
        return submitResult;
      },
      submitError => {
        ...
        const error = handleSubmitError(submitError);
        if (error || onSubmitFail) {
          return error;
        }
        throw submitError;
      }
    );
  }
  
  const afterValidation = () => {
    if (!zentForm.isValid()) {
      ...
      if (onSubmitFail) {
       handleOnSubmitError(new SubmissionError(validationErrors));
      }
    } else {
      return doSubmit();
    }
  };
  const allIsValidated = zentForm.fields.every(field => {
    return field.props.validateOnChange || field.props.validateOnBlur;
  });
  if (allIsValidated) {
    // 不存在没有进行过同步校验的field
    afterValidation();
  } else {
    zentForm.validateForm(true, afterValidation);
  }
}

使用方式如下:

const { handleSubmit } = this.props;
<Form onSubmit={handleSubmit(this.submit)} horizontal>

ZentForm不足之处

ZentForm虽然功能强大,但仍有一些待改进之处:

  1. 父组件维护了所有字段的实例,直接调用实例的方法来取值或者验证。这种方式虽然简便,但有违React声明式编程和函数式编程的设计思想,并且容易产生副作用,在不经意间改变了字段的内部属性。

  2. 大部分的组件重使用了shouldComponentUpdate,并对state和props进行了深比较,对性能有比较大的影响,可以考虑使用PureComponent。

  3. 太多的情况下对整个表单字段进行了校验,比较合理的情况应该是某个字段修改的时候只校验本身,在表单提交时再校验所有的字段。

  4. 表单提交操作略显繁琐,还需要调用一次handleSubmit,不够优雅。

结语

本文讨论了Form表单组件设计的思路,并结合有赞的ZentForm组件介绍具体的实现方式。ZentForm的功能十分强大,本文只是介绍了其核心功能,另外还有表单的异步校验、表单的格式化和表单的动态添加删除字段等高级功能都还没涉及到,感兴趣的朋友可点击前面的链接自行研究。

相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!

推荐阅读:

怎样使用Vue实现树形视图数据

JS对DOM树实现遍历有哪些方法

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn