表单操做在React中一直是一件比较繁琐的事情,使用formik能够极大的简化表单操做,formik的slogan就是
Build forms in React, without the tears 😭
javascript
在介绍formik以前,咱们先来讲说平常操做form表单中的一些痛点。html
当咱们想要访问表单中的input控件的值时,一般有两种操做,一种是使用ref去获得dom结构,第二种是使用state存储value。首先,针对于第一种状况,虽然使用ref可以让咱们很方便的获取到dom节点,进而获取input的值,可是须要注意的是当咱们在使用React的时候,咱们须要时刻告诉本身,不要直接操做dom!不要直接操做dom!不要直接操做dom!官方也是不推荐该作法的; 对于第二种作法,虽然这是官方推荐的作法,当时因为绑定了state,因此form表单中的控件也变成了受控组件,若是咱们想真正改动其数据,咱们每每须要再绑定一个onChange事件,当控件过多时,操做也是很是繁琐的。java
而formik的出现,将表单操做化繁为简,使得表单操做变得很是的简单。formik不只提供了针对的表单的状态管理,还能很方便的对表单作规则校验。其实formik的原理很简单:它内置了表单的state管理操做,无需咱们在单独为表单创建state,同时使用了Context,可以让表单组件多层嵌套,再也不须要咱们一层层传递。react
咱们以一个简单的例子来看看formik究竟是如何使用的,它又怎样简化了表单的操做。git
formik提供了一个名为Formik
的组件,组件包含了initialValues,validate,onSubmit,render等props(不限于此,列举出来的只是当前用到的关键的props,完整结构详解Formik API,下面解释一个各个prop做用:github
针对内部的表单,render方法会传入一个props,props包括了values,errors,handleSubmit,handleChange,handleBlur等属性(更详细的内容参见完整props,其中:api
示例代码以下:数组
<Formik
initialValues={{
name: "",
gender: "",
age: ""
}}
validate={values => {
let errors = {};
if (values.name.length === 0) {
errors.name = "Name can not be empty"
}
if (values.gender.length === 0) {
errors.gender = "You must choose a gender"
}
if (values.age <= 0 || values.age % 1 !== 0) {
errors.age = "Age must be a Positive Integer"
}
return errors;
}}
onSubmit={(values) => {
console.log(values)
}}
render={props =>
<form onSubmit={props.handleSubmit}>
<div className="content">
<div>
<label>姓名: </label><input type="text" id="name" name="name" value={props.values.name}
onChange={props.handleChange} onBlur={props.handleBlur}/>
{props.touched.name && props.errors.name && <div>{props.errors.name}</div>}
</div>
<div>
<label>性别: </label>
<input type="radio" id="male" value="male" name="gender" onChange={props.handleChange}
onBlur={props.handleBlur}/> <label htmlFor="male">男</label>
<input type="radio" id="female" value="female" name="gender" onChange={props.handleChange}
onBlur={props.handleBlur}/> <label htmlFor="female">女</label>
{props.touched.gender && props.errors.gender && <div>{props.errors.gender}</div>}
</div>
<div>
<label>年龄: </label><input type="number" id="age" name="age" value={props.values.age}
onChange={props.handleChange} onBlur={props.handleBlur}/>
{props.touched.age && props.errors.age && <div>{props.errors.age}</div>}
</div>
<div className="submit-area">
<button type="submit">提交</button>
</div>
</div>
</form>
}
/>
复制代码
注意:在输出错误信息的时候,一般有
touched.name&&errors.name
的判断,这是由于formik在作表单校验的时候,会将全字段都检查一遍,无论用户有没有操做,可是对于用户来讲,一般不但愿在尚未操做的字段显示一行错误信息,因此须要touched字段查看该字段是否已经操做过,操做过而且有错误,才显示。 经过例子能够看出,经过Formik组件传入初始字段,校验规则,提交事件;并在表单中绑定上handleSubmit,handleChange,handleBlur等事件后,咱们就可以实现一个简单表单操做。dom
可是咱们依旧能够看出在每个输入控件的地方,都重复的绑定了onChange,onBlur等事件,而且每一个输入错误信息的方式都是很重复的,这显得有点累赘。可是好在formik提交了Form,Field,ErrorMessage等封装好的组件,用于减小重复的工做,代码以下:学习
<Formik
......
//same as before
render={props =>
<Form>
<div className="content">
<div>
<label>姓名: </label><Field name="name"/>
<ErrorMessage name="name"/>
</div>
<div>
<label>性别: </label>
<Field name="gender" render={({field}) =>
<input type="radio" name={field.name} value="male" checked={field.value === "male"}
onChange={field.onChange}
onBlur={field.onBlur}/>
}/>
<label htmlFor="male">男</label>
<Field name="gender" render={({field}) =>
<input type="radio" name={field.name} value="female" checked={field.value === "female"}
onChange={field.onChange}
onBlur={field.onBlur}/>
}/> <label htmlFor="female">女</label>
<ErrorMessage name="gender"/>
</div>
<div>
<label>年龄: </label><Field name="age"/>
<ErrorMessage name="age"/>
</div>
<div className="submit-area">
<button type="submit">提交</button>
</div>
</div>
</Form>
}
/>
复制代码
能够看出,使用formik提供的组件还能进一步简化代码,经过对比能够看出,Form组件想到与form加上onSubmit;ErrorMessage组件就是将touched,errors等一系列判断包裹了起来;Field组件则是封装了onChange,onBlur,value等属性,须要注意的是对于radio,checkbox,select等控件须要单独作一些特殊处理,一般使用其render方法来自定义,除此以外,由于有了render方法,使得Field也能很好的和其余的第三方封装的控件结合。
此外,formik针对数组类型属性也有很好的支持,如今让咱们在表单里面加一个字段,表单样式以下:
咱们为表单添加了一个addresses字段,它是一个存储地址的数据,它的验证规则是至少有一个非空的字段。
formik提供了一个FieldArray的组件专门处理数组类型字段,代码以下:
<Formik
initialValues={{
name: "",
gender: "",
age: "",
addresses: [""]
}}
validate={values => {
let errors = {};
if (values.name.length === 0) {
errors.name = "Name can not be empty"
}
if (values.gender.length === 0) {
errors.gender = "You must choose a gender"
}
if (values.age <= 0 || values.age % 1 !== 0) {
errors.age = "Age must be a Positive Integer"
}
let addresses = values.addresses.filter(address => address.trim() !== "");
if (addresses.length === 0) {
errors.addresses = "Addresses must have 1 address at least"
}
return errors;
}}
onSubmit={(values) => {
console.log(values)
}}
render={props =>
<Form>
<div className="content">
......
// same as before
<div>
<label>地址: </label>
<FieldArray name="addresses" render={arrayHelper =>
<div>
{props.values.addresses.map((address, index) =>
<div className="address">
<Field name={`addresses.${index}`}/>
<div className="address_add" onClick={() => {
arrayHelper.push("")
}}>+
</div>
</div>
)}
</div>
}/>
<ErrorMessage name="addresses" component="div" className="error"/>
</div>
<div className="submit-area">
<button type="submit">提交</button>
</div>
</div>
</Form>
}
/>
复制代码
能够看到,使用FieldArray时,它会提供一个arrayHelper的参数,咱们能够经过arrayHelper很方便去操做数组的数组,如push,pop等等(更详细的参见完整arrayHelper API)
到此为止,就是formik的基本功能的介绍了。
虽然formik已经极大的方便了表单的操做,可是咱们仍是能够看到在作表单验证的时候,写的代码仍是比较繁琐,这个时候就须要yup出场了,yup的专长就是作规则校验。而且聪明的formik做者在设计formik的时候就让其很好的支持yup了。
yup的官方介绍:
Yup is a JavaScript object schema validator and object parser.
在Formik组件里有一个名为validationSchema的prop,这个就是为yup留出的接口。 话很少说,先show代码:
//没有yup的状况
<Formik
validate={values => {
let errors = {};
if (values.name.length === 0) {
errors.name = "Name can not be empty"
}
if (values.gender.length === 0) {
errors.gender = "You must choose a gender"
}
if (values.age <= 0 || values.age % 1 !== 0) {
errors.age = "Age must be a Positive Integer"
}
let addresses = values.addresses.filter(address => address.trim() !== "");
if (addresses.length === 0) {
errors.addresses = "Addresses must have 1 address at least"
}
return errors;
}}
//......
/>
//使用yup的状况
<Formik
validationSchema={Yup.object().shape({
name: Yup.string().trim().required("Name can not be empty"),
gender: Yup.string().required("You must choose a gender"),
age: Yup.number().moreThan(0, "Age must be greater than 0").integer("Age must be a integer"),
addresses: Yup.array().compact().min(1, "Addresses must have 1 address at least")
})}
//......
/>
复制代码
能够看到使用Yup后,不论是从代码量,仍是可读性来看都提交了不少,尤为是在表单很是复杂时,这之间的优化会很是的明显。
能够看到Yup的操做很是的语义化,因此学习成本很是的低,你只须要知道它的api,就能很轻易的使用它,因此对Yup的使用再也不赘述,详细信息参见Yup文档。
本文也只是对formik+yup作了简单的入门介绍,只作抛砖引玉。其实他们的厉害之处不只于此,你们能够尽情的探索。 如想查看源码点击表单代码连接
若是对本文有什么意见和建议,欢迎讨论和指正!!!