-
Notifications
You must be signed in to change notification settings - Fork 1
ReactNative表单验证探究(一)
在任何系统的开发工作中,表单的验证都是一项必不可少的工作,在ReactNative的开发过程中也不过如此。
由于ReactNative原生并没有提供表单验证功能,因此只能求助于第三方的一些插件,目前比较常用的ReactNative表单验证插件有以下几种:
-
react-native-gifted-form是一款非常棒的ReactNative表单验证插件,页面效果非常酷炫,上手也不是很难;但是该项目作者已经很长时间没有维护,并且表单控件只能使用该插件提供的一些控件。
-
tcomb-form-native是一款非常容易上手的ReactNative表单验证插件,star也达到了将近3k;同样它的缺点也是表单控件只能使用该插件提供的一些控件,并且表单控件的效果与我们的需求差异很大。
-
rc-form是antd所推荐的一款表单验证插件,它支持ReactNative,可以使用自己封装的表单控件;但是看完官方给出的demo之后,完全懵逼,瞬间感觉遇到了一块硬骨头。
以上三种表单验证插件都是非常棒的,各有优缺点,关于要使用哪一种完全取决于自己的业务需求,由于目前的业务需求,我选择了rc-form。
先来看下官方给出的demo,代码如下:
import React from 'react'
import PropTypes from 'prop-types'
import {
StyleSheet,
Button,
Dimensions,
TextInput,
Text,
View,
Alert,
} from 'react-native'
import { createForm } from 'rc-form'
const { width } = Dimensions.get('window')
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
padding: 50,
justifyContent: 'center',
},
inputView: {
width: width - 100,
paddingLeft: 10,
},
input: {
height: 42,
fontSize: 16,
},
errorinfo: {
marginTop: 10,
},
errorinfoText: {
color: 'red',
},
})
class FromItem extends React.PureComponent {
getError = error => {
if (error) {
return error.map(info => {
return (
<Text style={styles.errorinfoText} key={info}>
{info}
</Text>
)
})
}
}
render() {
const { label, onChange, value, error } = this.props
return (
<View style={styles.inputView}>
<TextInput
style={styles.input}
value={value || ''}
label={`${label}:`}
duration={150}
onChangeText={onChange}
highlightColor="#40a9ff"
underlineColorAndroid="#40a9ff"
/>
<View style={styles.errorinfo}>{this.getError(error)}</View>
</View>
)
}
}
class App extends React.Component {
static propTypes = {
form: PropTypes.object.isRequired,
}
checkUserNameOne = (value, callback) => {
setTimeout(() => {
if (value === '15188888888') {
callback('手机号已经被注册')
} else {
callback()
}
}, 2000)
}
submit = () => {
this.props.form.validateFields((error) => {
if (error) return
Alert('通过了所有验证') // eslint-disable-line new-cap
})
}
render() {
const { getFieldDecorator, getFieldError } = this.props.form
return (
<View style={styles.container}>
<Text>简单的手机号验证</Text>
{getFieldDecorator('username', {
validateFirst: true,
rules: [
{ required: true, message: '请输入手机号!' },
{
pattern: /^1\d{10}$/,
message: '请输入正确的手机号!',
},
{
validator: (rule, value, callback) => {
this.checkUserNameOne(value, callback);
},
message: '手机号已经被注册!',
},
],
})(
<FromItem
autoFocus
placeholder="手机号"
error={getFieldError('username')}
/>
)}
<Button color="#40a9ff" onPress={this.submit} title="登陆" />
</View>
)
}
}
export default createForm()(App)
看完这段代码,相信大部分人跟我的感觉是一样的(不就是一个表单验证吗,怎么使用起来这么复杂,让我瞬间怀念起使用Vue的那段日子)。之前做过Vue相关的开发,elementUI和iView中的表单组件使用起来是多么的简洁易懂,怎么到了ReactNative中就变的这么的复杂难懂。先不吐槽了~,来梳理一下这个demo中的思路吧,毕竟思路清晰以后我们还是可以将其再次封装的(毕竟再好用的插件也都是人家进行多次封装之后供我们使用的,这也是一次技术提升的好机会)。
我先来讲一下大致的思路,在代码的最后一句,导出了createForm()(App)
,createForm是一个React高阶函数,它接收一个React组件(App),在函数内部对该组件进行加工改造,最后返回加工改造之后的新组件。在createForm函数中其将form对象传递给App组件,因此通过this.props.form就可以获取到这个form对象。
const { getFieldDecorator, getFieldError } = this.props.form
....
{getFieldDecorator('username', {
validateFirst: true,
rules: [
{ required: true, message: '请输入手机号!' },
{
pattern: /^1\d{10}$/,
message: '请输入正确的手机号!',
},
{
validator: (rule, value, callback) => {
this.checkUserNameOne(value, callback);
},
message: '手机号已经被注册!',
},
],
})(
<FromItem
autoFocus
placeholder="手机号"
error={getFieldError('username')}
/>
)}
...
form提供了getFieldDecorator
函数。其函数定义形式为
getFieldDecorator(fieldName, fieldOption) => (Component) => ComponentWithExtraProps
getFieldDecorator函数接收两个参数,分别是
- fieldName:字段名称
- fieldOption:字段操作对象
源码
/**
* {getFieldDecorator(name,fieldOption)(<FormItem {...props}/>)}将表单项包装为高阶组件后返回
* 实现功能同getFieldProps方法,内部也调用getFieldProps方法
* 与getFieldProps方法不同的是,被封装表单项的props作为this.fieldMeta[name]的originalProps属性
* originalProps属性的主要目的存储被封装表单项的onChange事件,fieldOption下无同类事件时,执行该事件
* 不推荐将value、defaultValue作为表单项组件如FormItem的props属性
*/
getFieldDecorator: function getFieldDecorator(name, fieldOption) {
var _this = this;
// 获取需要传递给被修饰元素的属性。包括onChange,value等
// 同时在该props中设定用于收集元素值得监听事件(onChange),以便后续做双向数据。
var props = this.getFieldProps(name, fieldOption);
return function (fieldElem) {
// 此处fieldStore存储字段数据信息以及元数据信息。
// 数据信息包括value,errors,dirty等
// 元数据信息包括initValue,defaultValue,校验规则等。
var fieldMeta = _this.getFieldMeta(name);
var originalProps = fieldElem.props;
...
fieldMeta.originalProps = originalProps;
fieldMeta.ref = fieldElem.ref;
return _react2["default"].cloneElement(fieldElem, (0, _extends3["default"])({}, props, _this.getFieldValuePropValue(fieldMeta)));
};
};
其返回值是一个React高阶函数,接收参数是FormItem自定义组件,在该高阶函数中将onChange以及value传递给FormItem,然后在FormItem组件中
...
const { label, onChange, value, error } = this.props
return (
<View style={styles.inputView}>
<TextInput
style={styles.input}
value={value || ''}
label={`${label}:`}
duration={150}
onChangeText={onChange}
highlightColor="#40a9ff"
underlineColorAndroid="#40a9ff"
/>
<View style={styles.errorinfo}>{this.getError(error)}</View>
</View>
)
...
获取到onChange以及value后,将其绑定在TextInput组件的onChangeText和value上。(TextInput可以替换为其他的组件,但是替换的组件一定要有value和onChange属性)
form同样还提供了getFieldError
函数。其函数定义形式为
getFieldError(fieldName) => errors数组
getFieldError函数接收一个参数:
- fieldName:字段名称(要与getFieldDecorator函数的第一个参数fieldName保持一致)
源码
// 获取this.fields[name]["errors"]错误数据,并剔除没有error.message的错误数据
getFieldError: function getFieldError(name) {
return (0, _utils.getErrorStrs)(this.getFieldMember(name, 'errors'));
},
// 获取this.fields[name][member]属性数据
getFieldMember: function getFieldMember(name, member) {
var field = this.getField(name);
return field && field[member];
},
其返回值是一个数组,数组中存储的是错误信息,拿到错误信息之后,将其传递给FormItem的error属性
...
<FromItem
...
error={getFieldError('username')}
/>
...
// 在FromItem.js中
...
const { label, onChange, value, error } = this.props
return (
<View style={styles.inputView}>
...
<View style={styles.errorinfo}>{this.getError(error)}</View>
</View>
)
...
// 返回error信息组件函数
getError = error => {
if (error) {
return error.map(info => {
return (
<Text style={styles.errorinfoText} key={info}>
{info}
</Text>
)
})
}
}
在FromItem组件中,获取到error数组对象之后,调用error信息展示函数,该函数返回错误信息展示组件,从而将错误信息展示给用户。
以上就是在ReactNative中使用rc-form的基本思路,想必大家看了demo代码之后一定在想一个问题,如果页面中需要表单验证的组件非常多,那么render函数中的代码量也是非常庞大和冗余的,怎么将这些冗余的代码给抽离出来,怎么给render函数瘦身,这些问题我会在ReactNative表单验证(二)中进行讲解,敬请期待~