您好,欢迎访问一九零五行业门户网

React Form组件封装步骤详解

这次给大家带来react form组件封装步骤详解,react form组件封装的注意事项有哪些,下面就是实战案例,一起来看一下。
前言
对于网页系统来说,表单提交是一种很常见的与用户交互的方式,比如提交订单的时候,需要输入收件人、手机号、地址等信息,又或者对系统进行设置的时候,需要填写一些个人偏好的信息。 表单提交是一种结构化的操作,可以通过封装一些通用的功能达到简化开发的目的。本文将讨论form表单组件设计的思路,并结合有赞的zentform组件介绍具体的实现方式。本文所涉及的代码都是基于react v15的版本。
form组件功能
一般来说,form组件的功能包括以下几点:
表单布局
表单字段
封装表单验证&错误提示
表单提交
下面将对每个部分的实现方式做详细介绍。
表单布局
常用的表单布局一般有3种方式:
行内布局
水平布局
垂直布局
实现方式比较简单,嵌套css就行。比如form的结构是这样:
<form class="form">   <label class="label"/>   <field class="field"/> </form>
对应3种布局,只需要在form标签增加对应的class:
<!--行内布局--> <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>
相应的,要定义3种布局的css:
.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;   ... }
表单字段封装
字段封装部分一般是对组件库的组件针对form再做一层封装,如input组件、select组件、checkbox组件等。当现有的字段不能满足需求时,可以自定义字段。
表单的字段一般包括两部分,一部分是标题,另一部分是内容。zentform通过getcontrolgroup这一高阶函数对结构和样式做了一些封装,它的入参是要显示的组件:
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>      );                             } }
这里用到的label和error等信息,是通过field组件传入的:
<field label="预约门店:" name="dept" component={customizedcomp} validations={{ required: true, }} validationerrors={{ required: '预约门店不能为空', }} required />
这里的customizedcomp是通过getcontrolgroup封装后返回的组件。
字段与表单之间的交互是一个需要考虑的问题,表单需要知道它包含的字段值,需要在适当的时机对字段进行校验。zentform的实现方式是在form的高阶组件内维护一个字段数组,数组内容是field的实例。后续通过操作这些实例的方法来达到取值和校验的目的。
zentform的使用方式如下:
class fieldform extends react.component {   render() {     return (       <form>         <field name="name" component={customizedcomp} </form>     )   } } export default createform()(fieldform);
其中form和field是组件库提供的组件,customizedcomp是自定义的组件,createform是组件库提供的高阶函数。在createform返回的组件中,维护了一个fields的数组,同时提供了attachtoform和detachfromform两个方法,来操作这个数组。这两个方法保存在context对象当中,field就能在加载和卸载的时候调用了。简化后的代码如下:
/**  * 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, {...});   } }
当需要获取表单字段值的时候,只需要遍历fields数组,再调用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;   }; }
表单验证&错误提示
表单验证是一个重头戏,只有验证通过了才能提交表单。验证的时机也有多种,如字段变更时、鼠标移出时和表单提交时。zentform提供了一些常用的验证规则,如非空验证,长度验证,邮箱地址验证等。当然还能自定义一些更复杂的验证方式。自定义验证方法可以通过两种方式传入zentform,一种是通过给createform传参:
createform({   formvalidations: {     rule1(values, value){     },     rule2(values, value){     },   } })(formcomp);
另一种方式是给field组件传属性:
<field validations={{ rule1(values, value){ }, rule2(values, value){ }, }} validationerrors={{ rule1: 'error1', rule2: 'error2' }} />
使用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的validateonchange和validateonblur来改变校验时机。
<field validateonchange={false} validateonblur={false} validations={{ required: true, matchregex: /^[a-za-z]+$/ }} validationerrors={{ required: '值不能为空', matchregex: '只能为字母' }} />
对应的,在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来告诉表单哪些字段需要同步进行验证。
表单提交
表单提交时,一般会经历如下几个步骤
表单验证
表单提交
提交成功处理
提交失败处理
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虽然功能强大,但仍有一些待改进之处:
父组件维护了所有字段的实例,直接调用实例的方法来取值或者验证。这种方式虽然简便,但有违react声明式编程和函数式编程的设计思想,并且容易产生副作用,在不经意间改变了字段的内部属性。
大部分的组件重使用了shouldcomponentupdate,并对state和props进行了深比较,对性能有比较大的影响,可以考虑使用purecomponent。
太多的情况下对整个表单字段进行了校验,比较合理的情况应该是某个字段修改的时候只校验本身,在表单提交时再校验所有的字段。
表单提交操作略显繁琐,还需要调用一次handlesubmit,不够优雅。
结语
本文讨论了form表单组件设计的思路,并结合有赞的zentform组件介绍具体的实现方式。zentform的功能十分强大,本文只是介绍了其核心功能,另外还有表单的异步校验、表单的格式化和表单的动态添加删除字段等高级功能都还没涉及到,感兴趣的朋友可点击前面的链接自行研究。
相信看了本文案例你已经掌握了方法,更多精彩请关注其它相关文章!
推荐阅读:
js callback回调函数使用案例详解
react navigation实战中有哪些注意事项
以上就是react form组件封装步骤详解的详细内容。
其它类似信息

推荐信息