问题
上一篇我们实现一个最基本的表单验证。
如果我们想要再写一个添加图书的功能,以同样的方式去写就会发现会出现很多重复的代码。因为所有的验证代码都是耦合在组件中的,没有办法实现复用。
重复代码是混乱的根源!
既然我们用了React,那我们是不是可以用组件化的方式去给表单验证这些逻辑解耦呢?
高阶组件:formProvider
什么是高阶组件?
高阶组件就是返回组件的组件(函数)
为什么要通过一个组件去返回另一个组件?
使用高阶组件可以在不修改原组件代码的情况下,修改原组件的行为或增强功能。
我们现在已经有了带有表单校验功能的添加用户的表单,这里的表单有3个字段:name、age、gender,并且每个字段都有它自己的校验规则和对应的错误信息。
要做一个添加图书的功能,图书的表单有name、price、owner_id三个字段,一样地,每个字段有它自己的校验规则和错误信息。
仔细想想,每当我们需要写一个表单的时候,都需要有一个地方来保存表单字段的值(state),有一个函数来处理表单值的更新和校验(handleValueChange),这些东西我们可以用高阶组件来封装。
而添加用户的表单和添加图书的表单之间的不同之处仅仅是表单字段以及字段的默认值、校验规则和错误信息。
那么我们的高阶组件模型就出来了:
function formProvider (fields) {
return function (Comp) {
constructor (props) {
super(props);
this.state = {
form: {...},
formValid: false // 加了一个formValid用来保存整个表单的校验状态
};
}
handleValueChange (field, value) {...}
class FormComponent extends React.Component {
render () {
const {form, formValid} = this.state;
return (
<Comp {
...this.props} form={form} formValid={formValid} onFormChange={this.handleValueChange}/>
);
}
}
return FormComponent;
}
}
formProvider接收一个fields参数,并返回一个函数,这个函数接收一个组件作为参数并返回一个组件,所以它的用法是这样的:
UserAdd = formProvider(fields)(UserAdd);
经过formProvider处理后的UserAdd组件会得到额外的props:
- form
- formValid
- onFormChange
在/src
下新建一个目录utils
,新建formProvider.js
文件,写入具体的代码实现:
import React from 'react';
function formProvider (fields) {
return function (Comp) {
const initialFormState = {};
for (const key in fields) {
initialFormState[key] = {
value: fields[key].defaultValue,
error: ''
};
}
class FormComponent extends React.Component {
constructor (props) {
super(props);
this.state = {
form: initialFormState,
formValid: false
};
this.handleValueChange = this.handleValueChange.bind(this);
}
handleValueChange (fieldName, value) {
const { form } = this.state;
const newFieldState = {value, valid: true, error: ''};
const fieldRules = fields[fieldName].rules;
for (let i = 0; i < fieldRules.length; i++) {
const {pattern, error} = fieldRules[i];
let valid = false;
if (typeof pattern === 'function') {
valid &