Formik 介绍和基本使用
介绍
Formik 是 React 官方推荐使用的用于增强表单处理能力,简化表单处理流程的第三方模块。
让开发者更建单的开发表单,更专注于业务。
包含验证、追踪访问字段以及处理表单提交的完整解决方案。
基本使用
使用 formik 进行表单数据绑定及表单提交处理。
只需要使用 formik 创建一些内容,然后将这些内容与 form 表单进行绑定即可:
- formik 提供一个 useFormik 方法用于创建表单内容,它接收一个配置对象,可以配置:
- initialValues:表单的默认数据
- onSubmit:表单提交事件处理函数,它接收表单数据作为参数
- 不需要手动书写阻止表单默认行为的代码,默认 formik 进行了阻止
- formik 创建的内容用于表单绑定,包含
- values:表单数据
- handleSubmit:表单提交事件处理函数
- handleChange:表单项的 onChange 事件处理函数,用于数据同步
- 每个表单项通过 name、value、onChange 与 formik 创建的内容进行绑定
# 安装
npm install formik
import { useFormik } from 'formik'
function App() {
const formik = useFormik({
initialValues: {
username: '张三',
password: '123456'
},
onSubmit: values => {
console.log(values)
}
})
return (
<form onSubmit={formik.handleSubmit}>
<div>
<input
type="text"
name="username"
value={formik.values.username}
onChange={formik.handleChange}
/>
</div>
<div>
<input
type="password"
name="password"
value={formik.values.password}
onChange={formik.handleChange}
/>
</div>
<input type="submit" />
</form>
)
}
export default App
Formik 表单验证
初始验证
useFormik
可以配置表单验证方式 validate
。
validate
是一个函数,接收表单数据作为参数。
validate
要求返回一个对象,对象的 key
是验证不通过的表单项的 name
,value
是验证失败的提示信息。
useFormik
返回的对象包含 errors
,就是验证方法返回的结果。
import { useFormik } from 'formik'
function App() {
const formik = useFormik({
initialValues: {
username: '张三',
password: '123456'
},
validate: values => {
const errors = {}
const {username, password} = values
if (!username){
errors.username = '请输入用户名'
} else if (username.length > 15) {
errors.username = '用户名长度不得超过15个字'
}
if (!password) {
errors.password = '请输入密码'
} else if (password.length < 6) {
errors.password = '密码长度不得小于6'
}
return errors
},
onSubmit: values => {
console.log(values)
}
})
return (
<form onSubmit={formik.handleSubmit}>
<div>
<input
type="text"
name="username"
value={formik.values.username}
onChange={formik.handleChange}
/>
</div>
{formik.errors.username && <p>{formik.errors.username}</p>}
<div>
<input
type="password"
name="password"
value={formik.values.password}
onChange={formik.handleChange}
/>
</div>
{formik.errors.password && <p>{formik.errors.password}</p>}
<input type="submit" />
</form>
)
}
export default App
优化体验
体验问题:将表单默认数据设置为空,在输入用户名(焦点还未离开、密码还未改动)的时候,触发了验证,由于密码验证失败,显示了错误提示。
优化:初始数据未变更的表单项不进行校验。
方法:
- 开启离开焦点时触发验证
useFormik
返回的对象包含handleBlur
方法,将其绑定到表单项的onBlur
属性上,在表单失去焦点的时候会触发验证。
- 提示信息时检查表单元素的值是否被改动过
useFormik
返回对象中包含touched
属性,它是一个对象,存储哪些表单数据发生了变化。- 表单项的值发生变化后,不论是否还原为初始值,
touched
中的标记不会再改变。 - 默认是
{}
,如果username
表单项发生了变化,则会记录为{username: true}
- 只有当表单项绑定了
handleBlur
事件处理函数后,touched
才会记录
import { useFormik } from 'formik'
function App() {
const formik = useFormik({
initialValues: {
username: '',
password: ''
},
validate: values => {
const errors = {}
const {username, password} = values
if (!username){
errors.username = '请输入用户名'
} else if (username.length > 15) {
errors.username = '用户名长度不得超过15个字'
}
if (!password) {
errors.password = '请输入密码'
} else if (password.length < 6) {
errors.password = '密码长度不得小于6'
}
return errors
},
onSubmit: values => {
console.log(values)
}
})
console.log(formik.touched)
return (
<form onSubmit={formik.handleSubmit}>
<div>
<input
type="text"
name="username"
value={formik.values.username}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
</div>
{(formik.touched.username && formik.errors.username) && <p>{formik.errors.username}</p>}
<div>
<input
type="password"
name="password"
value={formik.values.password}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
</div>
{(formik.touched.password && formik.errors.password) && <p>{formik.errors.password}</p>}
<input type="submit" />
</form>
)
}
export default App
Yup - 更优雅的表单验证
Yup 是一个 JavaScript 方案生成器,用于值的解析转化和验证。
# 安装
npm install yup
useFormik
的配置选项中通过 validationSchema
配置验证规则。
object()
创建验证规则,传入一个对象,key
是表单项的 name
,值是 yup 的验证方法,例如:
import { useFormik } from 'formik'
import * as Yup from 'yup'
function App() {
const formik = useFormik({
initialValues: {
username: '',
password: ''
},
validationSchema: Yup.object({
username: Yup.string() // 将值(不为空时)转化为字符串
.max(15, '用户名长度不得超过15个字') // 验证长度
.required('请输入用户名'), // 验证必传
password: Yup.string()
.min(6, '密码长度不得小于6')
.required('请输入密码')
}),
onSubmit: values => {
console.log(values)
}
})
return (
<form onSubmit={formik.handleSubmit}>
<div>
<input
type="text"
name="username"
value={formik.values.username}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
</div>
{(formik.touched.username && formik.errors.username) && <p>{formik.errors.username}</p>}
<div>
<input
type="password"
name="password"
value={formik.values.password}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
</div>
{(formik.touched.password && formik.errors.password) && <p>{formik.errors.password}</p>}
<input type="submit" />
</form>
)
}
export default App
getFieldProps - 减少样板代码
useFormik
返回的对象提供了一个方法:getFieldProps
,可以获取指定表单项的 name
、value
、onChange
、onBlur
属性,可以将它们直接绑定到表单项元素,减少重复代码。
// 样板代码
name="username"
value={formik.values.username}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
import { useFormik } from 'formik'
import * as Yup from 'yup'
function App() {
const formik = useFormik(...)
console.log(formik.getFieldProps('username'))
return (
<form onSubmit={formik.handleSubmit}>
<div>
<input
type="text"
{...formik.getFieldProps('username')}
/>
</div>
{(formik.touched.username && formik.errors.username) && <p>{formik.errors.username}</p>}
<div>
<input
type="password"
{...formik.getFieldProps('password')}
/>
</div>
{(formik.touched.password && formik.errors.password) && <p>{formik.errors.password}</p>}
<input type="submit" />
</form>
)
}
export default App
使用组件的方式构建表单
Formik 提供了一系列组件去构建表单,一些细节代码,已经在组件中封装好了。
- Formik:表单外层组件,用来包裹 Form 组件
- 通过属性传递配置项,例如
- initialValues
- onSubmit
- validationSchema
- 通过属性传递配置项,例如
- Form:表单组件,在内部包裹一个个具体的表单项
- Field:表单项组件
name
属性绑定目标表单项的name
- 默认 Field 渲染为一个文本框
- ErrorMessage:用于显示表单验证失败的提示信息
name
属性绑定目标表单项的name
import { Formik, Form, Field, ErrorMessage } from 'formik'
import * as Yup from 'yup'
function App() {
const initialValues = {
username: '',
password: ''
}
const validationSchema = Yup.object({
username: Yup.string()
.max(15, '用户名长度不得超过15个字')
.required('请输入用户名'),
password: Yup.string()
.min(6, '密码长度不得小于6')
.required('请输入密码')
})
const onSubmit = values => {
console.log(values)
}
return (
<Formik initialValues={initialValues} validationSchema={validationSchema} onSubmit={onSubmit}>
<Form>
<Field name="username" />
<ErrorMessage name="username" />
<Field name="password" />
<ErrorMessage name="password" />
<input type="submit" />
</Form>
</Formik>
)
}
export default App
构建其它表单控件
默认情况下,Field 组件渲染的是文本框(text),可以通过 as
属性指定表单元素类型。
import { Formik, Form, Field } from 'formik'
function App() {
const initialValues = {
content: '内容',
subject: 'JS'
}
const onSubmit = values => {
console.log(values)
}
return (
<Formik initialValues={initialValues} onSubmit={onSubmit}>
<Form>
<Field name="content" as="textarea" />
<Field name="subject" as="select">
<option value="Node">Node</option>
<option value="JS">JS</option>
</Field>
<input type="submit" />
</Form>
</Formik>
)
}
export default App
Field 组件并没有提供现成的 password
、单选框、复选框这样的表单控件,需要自己创建。
构建自定义表单控件
Formik 提供 useField
方法构建自定义表单控件,也就是自定义组件。
useField
可以获取表单项信息,返回一个数组,包含两项内容:
field
:包含表单属性相关的内容,name
,value
、onChange
、onBlur
等meta
:包含表单验证相关的信息
import { Formik, Form, Field, ErrorMessage, useField } from 'formik'
import * as Yup from 'yup'
function MyInputField({label, name, ...props}) {
const [field, meta] = useField({ name })
console.log('field', field);
console.log('meta', meta);
return <div>
{/* 由于 for 在 JavaScript 中是保留字,所以 React 元素中使用了 htmlFor 来代替。 */}
<label htmlFor={props.id}>{label}</label>
<input {...field} {...props} />
{meta.touched && meta.error ? <div>{meta.error}</div> : null}
</div>
}
function App() {
const initialValues = {
username: '',
password: ''
}
const validationSchema = Yup.object({
username: Yup.string()
.max(15, '用户名长度不得超过15个字')
.required('请输入用户名'),
password: Yup.string()
.min(6, '密码长度不得小于6')
.required('请输入密码')
})
const onSubmit = values => {
console.log(values)
}
return (
<Formik initialValues={initialValues} validationSchema={validationSchema} onSubmit={onSubmit}>
<Form>
<Field name="username" />
<ErrorMessage name="username" />
<MyInputField id="password" name="password" label="密码" type="password" placeholder="请输入密码" />
<input type="submit" />
</Form>
</Formik>
)
}
export default App
field
和 meta
打印结果:
构建自定义 checkbox 控件
- 复选框的 onChange 事件由开发者自定义,不使用 formik 生成的
- 不能直接修改复选框的值,需要通过
useField
返回的helper.setValue()
方法
import { Formik, Form, Field, ErrorMessage, useField } from 'formik'
import * as Yup from 'yup'
function MyCheckBox({ label, name, ...props }) {
const [field, meta, helper] = useField({ name })
const { value } = meta
const { setValue } = helper
console.log('meta', meta)
console.log('helper', helper)
const handleChange = () => {
const set = new Set(value)
if (set.has(props.value)) {
set.delete(props.value)
} else {
set.add(props.value)
}
setValue([...set])
}
return (
<div>
<label>
<input type="checkbox" name={name} checked={value.includes(props.value)} {...props} onChange={handleChange} /> {props.value}
</label>
</div>
)
}
function App() {
const initialValues = {
username: '',
hobbies: ['足球', '篮球']
}
const validationSchema = Yup.object({
username: Yup.string().max(15, '用户名长度不得超过15个字').required('请输入用户名')
})
const onSubmit = values => {
console.log(values)
}
return (
<Formik initialValues={initialValues} validationSchema={validationSchema} onSubmit={onSubmit}>
<Form>
<Field name="username" />
<ErrorMessage name="username" />
<MyCheckBox value="足球" label="足球" name="hobbies" />
<MyCheckBox value="篮球" label="篮球" name="hobbies" />
<MyCheckBox value="橄榄球" label="橄榄球" name="hobbies" />
<input type="submit" />
</Form>
</Formik>
)
}
export default App
meta
和 helper
打印结果: