Formik是一个专为React构建的开源表单库。它提供了一个易于使用的API来处理表单状态管理,表单验证以及表单提交。Formik支持React中的所有表单元素和事件,可以很好地与React生态系统中的其他库集成。同时,Formik还提供了一些高级功能,如支持异步验证、字段级别的验证、根据表单状态变化自动计算属性等。
Formik 基础
Formik的优势,相较于传统的表单处理方法,Formik具有以下优势:
- 状态管理: Formik自动地处理表单状态管理,无需手动编写大量的状态管理逻辑。
- 表单验证: Formik提供了易于使用的表单验证工具,可以快速实现表单验证逻辑。
- 表单提交: Formik可以方便地处理表单提交,包括异步表单提交和重试机制。
- 支持复杂表单: Formik支持处理包括多级嵌套、动态表单、数组表单等多种复杂表单。
Formik 安装和简单使用
1、Formik安装,使用npm或yarn安装Formik。在终端中,切换项目目录并运行此命令 npm install formik
或 yarn add formik
。
2、表单数据绑定以及提交处理
useFormik HOOK 参数说明:
- initialValues:传递定义的输入HTML元素的name属性相匹配的初始值
- onSubmit:提交程序,已经默认阻止了默认行为
onSubmit 属性定义了一个回调函数,在表单提交时被调用。在回调函数中可以访问表单中所有输入框的值并执行提交操作,通过 setSubmitting 函数,可以设置表单的提交状态。在表单的渲染函数中通过 handleSubmit 属性来处理表单的提交,使用 isSubmitting(默认false) 属性来禁用提交按钮,直到表单提交完成。
const formik = useFormik({
initialValues: {
username: '',
password: ''
},
onSubmit: (values, {setSubmitting}) => {
// formik 已经默认帮我们阻止了默认执行
console.log('values: ', values);
setSubmitting(true)
}
})
……
<form onSubmit={formik.handleSubmit}>
<div>
<label htmlFor="username">用户名:</label>
<input id="username" type="text" name="username" value={formik.values.username} onChange={formik.handleChange}></input>
</div>
<div>
<label htmlFor="password">密码:</label>
<input id="password" type="password" name="password" value={formik.values.password} onChange={formik.handleChange}></input>
</div>
<button type="submit" disabled={formik.isSubmitting}>提交</button>
</form>
useFormik 返回对象属性解释:
- handleSubmit:提交处理程序
- handleChange:传递给每个
<input>
、<select>
、 或<textarea>
的更改处理程序 - values:表单的当前值,使用 name 访问对应的值
- handleChange:每个 HTML 输入重用相同的更改处理函数
- setValues和setFieldValue:setValues设置formik的values属性值,setFieldValue设置values的某一个属性值,二者写法不同,但是目的一样改变values的值。
如果没有 formik,那么就得一个个写change事件。
3、表单校验
Formik 不仅跟踪表单values,还跟踪其验证和错误消息。要使用 JS 添加验证,需指定一个自定义验证函数并将其传递给钩子 useFormik()
的参数 validate。如果存在错误,这个自定义验证函数应该生成一个匹配的error对象。
validate: values => {
const errors = {}
if (!values.username) {
errors.username = '请输入用户名'
} else if (values.username.length < 6 || values.username.length > 16) {
errors.username = '请输入6~16位的用户名'
} else if (!/^\w{6,16}$/.test(values.username)) {
errors.username = '请输入由字母、数字、下划线组成的用户名'
}
if (!values.password) {
errors.password = '请输入密码'
} else if (values.password.length < 6 || values.password.length > 16) {
errors.password = '请输入6~16位的密码'
} else if (!/^\w{6,16}$/.test(values.password)) {
errors.password = '请输入由字母、数字、下划线组成的密码'
}
console.log('errors: ', errors);
return errors
}
默认情况下,Formik 将在每次击键(onChange)事件以及提交之前进行验证。传递 formik.handleBlur
给输入元素的 onBlur
属性,那么会在输入元素的失焦(onBlur)事件中进行验证:
<input id="username" type="text" name="username" value={formik.values.username} onChange={formik.handleChange} onBlur={formik.handleBlur}></input>
校验确实实现了,但是如果希望在提交表单的时候再显示错误,需要怎么做呢?
Formik 会跟踪哪些字段已被访问过。它将这些信息存储在一个名为 touched 的对象中,这些值为boolean值。
<p>{formik.touched.username && formik.errors.username ? formik.errors.username : ''}</p>
Formik 结合 Yup 进行表单验证
Yup是一个构建对象模式的JavaScript模式验证器,用于验证和解析数据。它提供了一种声明式方法来创建校验模式。
由于 Formik 作者/用户非常喜欢Yup,因此 Formik 有一个名为 Yup 的特殊配置道具validationSchema,它会自动将 Yup 的验证错误消息转换为一对象。
1、Yup 安装 npm install yup
或 yarn add yup
2、使用 Yup 校验表单:
- Formik 结合 Yup 可以轻松实现表单验证
validationSchema={Yup.object({...})}
,使用 validationSchema 和 Yup 搭配之后就不需要再使用validate配置了。 - Yup 允许创建复杂的校验逻辑,如必填项、字符长度、正则表达式等。
validationSchema: Yup.object({
username: Yup.string().required('请输入用户名')
.min(6, '请输入6~16位的用户名')
.max(16, '请输入6~16位的用户名')
.matches(/^\w{6,16}$/, '请输入由字母、数字、下划线组成的用户名'),
password: Yup.string().required('请输入密码')
.min(6, '请输入6~16位的密码')
.max(16, '请输入6~16位的密码')
.matches(/^\w{6,16}$/, '请输入由字母、数字、下划线组成的密码')
})
Formik 组件
以上都是通过 useFormik 拿到控制formik表单的内容,但是这样不容易形成封装,也就是说无法进行实例传值。而Formik由于配备了React Context,因此Formik本身就可以管理所包裹的JSX。
formik完全遵守React的组件化原则,可以和其他库或自定义逻辑无缝集成:
- formik支持构建和复用表单组件,通过
<Formik />
组件和Context API实现跨组件通信。 - 可以创建包装
<Field />
、ErrorMessage
等的自定义组件,提高代码复用性和可维护性。
<Formik
initialValues={{username: '', password: ''}} // 设置初始化值
onSubmit={(values, {setSubmitting}) => { // 提交表单执行的函数
setTimeout(() => {
console.log('values: ', values);
setSubmitting(true)
}, 2000);
}}
validationSchema={Yup.object({ // 设置表单校验的模式
username: Yup.string().required('请输入用户名')
.min(6, '请输入6~16位的用户名')
.max(16, '请输入6~16位的用户名')
.matches(/^\w{6,16}$/, '请输入由字母、数字、下划线组成的用户名'),
password: Yup.string().required('请输入密码')
.min(6, '请输入6~16位的密码')
.max(16, '请输入6~16位的密码')
.matches(/^\w{6,16}$/, '请输入由字母、数字、下划线组成的密码')
})}
>
{({handleSubmit, values, touched, handleChange, handleBlur, errors, isSubmitting}) => {
return <form onSubmit={handleSubmit}>
<div>
<label htmlFor="username1">用户名:</label>
{/* <input id="username1" type="text" name="username" value={values.username} onChange={handleChange} onBlur={handleBlur}></input> */}
<Field id='username1' name='username' type='text'></Field>
{/* <p>{touched.username && errors.username ? errors.username : ''}</p> */}
<ErrorMessage name="username"></ErrorMessage>
</div>
<div>
<label htmlFor="password1">密码:</label>
<input id="password1" type="password" name="password" value={values.password} onChange={handleChange}></input>
<p>{touched.password && errors.password ? errors.password : ''}</p>
</div>
<button type="submit" disabled={isSubmitting}>提交</button>
</form>
}}
</Formik>
常用组件
1、ErrorMessage 捕捉错误的容器,必传一个name属性。
<ErrorMessage name="username"></ErrorMessage>
2、Field 将自动将输入连接到 Formik,默认渲染是input输入框,有以下几种方法渲染:
function MyField({field, form}) {
console.log('field: ', field);
console.log('form: ', form);
return <input {...field}></input>
}
……
<Formik
initialValues={{email: '', project: 2, phone: '', address: '1'}} // 设置初始化值
onSubmit={(values) => { console.log('values: ', values); }} // 提交表单执行的函数
validationSchema={Yup.object({ // 设置表单校验的模式
phone: Yup.string().required('请输入电话号码')
.matches(/^1[0-9]{10}/, '请输入有效的电话号码')
})}
>
{({handleSubmit}) => {
return <form onSubmit={handleSubmit}>
{/* 1、直接渲染 */}
<Field type='email' name='email' placeholder='请输入电子邮箱'></Field> <br/>
{/* 2、as 转化成其他节点 */}
<Field as='select' name='project'>
<option value={1}>数学及应用数学</option>
<option value={2}>软件工程</option>
<option value={3}>国际贸易</option>
</Field> <br/>
{/* 3、渲染 jsx */}
<Field name='phone'>
{({field, form, meta}) => {
console.log('field: ', field);
console.log('form: ', form);
console.log('meta: ', meta);
return <div>
<label htmlFor="phone">电话号码</label>
<input id="phone" type="text" placeholder="请输入电话号码" value={field.value.phone} {...field}></input>
<p>{ meta.touched && meta.error ? meta.error : ''}</p>
</div>
}}
</Field>
{/* 4、以 component 组件形式渲染 */}
<Field name='address' component={MyField}></Field> <br />
<button type="submit">提交</button>
</form>
}}
</Formik>
数组和对象校验
Formik 支持嵌套对象和数组:
- yup 对象校验使用
Yup.object({})
- yup 数组校验使用
Yup.array().of()
<Formik
initialValues={{social: {wechat: '', qq: ''}, frends: ['', '']}}
onSubmit={(values, {setSubmitting}) => {
console.log('values: ', values)
setSubmitting(true)
}}
validationSchema={Yup.object({
social: Yup.object({
wechat: Yup.string().required('请输入微信'),
qq: Yup.string().required('请输入QQ').matches(/^[\w]$/, '请输入有效的QQ')
}),
frends: Yup.array().of(Yup.string().required('请输入你的朋友'))
})}>
{({values, handleChange, handleBlur, touched, errors, isSubmitting, setValues, setFieldValue}) => {
console.log('object array values: ', values);
console.log('object array handleChange: ', handleChange);
console.log('object array handleBlur: ', handleBlur);
console.log('object array touched: ', touched);
console.log('object array errors: ', errors);
console.log('object array setValues: ', setValues);
console.log('object array setFieldValue: ', setFieldValue);
return <form>
<h4>社交账号</h4>
<label>微信:</label>
<input name="social.wechat" value={values.social.wechat} onChange={handleChange} onBlur={handleBlur}></input>
<p>{ touched.social && errors.social && touched.social.wechat && errors.social.wechat ? errors.social.wechat : ''}</p>
<br />
<label>QQ:</label>
<input name="social.qq" value={values.social.qq} onChange={handleChange} onBlur={handleBlur}></input>
<h4>朋友</h4>
{values.frends.map((v, i) => {
return (<div key={i}>
<input name={`friends[${i}]`} onChange={handleChange} onBlur={handleBlur}></input>
{i < values.frends.length && <br />}
</div>)
})}
<button type="submit" disabled={isSubmitting}>提交</button>
</form>}}
</Formik>