Formik + Yup 基本使用、表单验证、自定义控件

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 是验证不通过的表单项的 namevalue 是验证失败的提示信息。

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

优化体验

体验问题:将表单默认数据设置为空,在输入用户名(焦点还未离开、密码还未改动)的时候,触发了验证,由于密码验证失败,显示了错误提示。

优化:初始数据未变更的表单项不进行校验。

方法:

  1. 开启离开焦点时触发验证
    • useFormik 返回的对象包含 handleBlur 方法,将其绑定到表单项的 onBlur 属性上,在表单失去焦点的时候会触发验证。
  2. 提示信息时检查表单元素的值是否被改动过
    • 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,可以获取指定表单项的 namevalueonChangeonBlur 属性,可以将它们直接绑定到表单项元素,减少重复代码。

在这里插入图片描述

// 样板代码
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,valueonChangeonBlur
  • 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

fieldmeta 打印结果:

在这里插入图片描述

构建自定义 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

metahelper 打印结果:

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值