Formik(增强表单处理能力)

236 阅读6分钟

Formik简介

增强表单处理能⼒. 简化表单处理流程. 官⽹

Formik 下载

npm install formik

基本使用

使⽤ formik 进⾏表单数据绑定以及表单提交处理

  • useFormik 方法接收一个对象,返回值也是一个对象

    • initialValues为初始化数据值
    • onSubmit 为表单提交方法, 形参 values 表单提交行为获取的数据
  • 表单通过name进行绑定

  • handleSubmit 表单提交方法,自动绑定onSubmit

  • formik.handleChange 方法,用于同步对应表单值

import React from "react";
import { useFormik } from 'formik'

function App() {
  const formik = useFormik({
    initialValues:{username:"张三",password:"123456"},
    onSubmit:(values)=>{
      console.log(values) // 获取最新数据
    }
  })
  return (
  <form onSubmit={formik.handleSubmit}>
    <input
      type="text"
      name="username"
      value={formik.values.username}
      onChange={formik.handleChange}
     />
   
    <input
      type="password" 
      name="password"
      value={formik.values.password}
      onChange={formik.handleChange}
      />
    
    <input type="submit" />
  </form>
  )
}


export default App

表单验证

初始验证方式

  • validate 用于表单校验,返回一个自定义对象 errors, 在内部写验证函数
  • formik.errors 可以获取验证结果
import React from "react";
import { useFormik } from 'formik'

function App() {
  const formik = useFormik({
    initialValues:{username:"张三",password:"123456"},
    validate:values=>{
      const errors={}
      if(!values.username){
        errors.username='请输入用户名'
      }else if(values.username>15){
        errors.username='用户名的长度不能大于15'
      }
      if(values.password<6) {
        errors.password='密码的长度不能小于6'
      }
      return errors
    },
    onSubmit:(values)=>{
      console.log(values) // 获取最新数据
    }
  })
  return (
  <form onSubmit={formik.handleSubmit}>
    <input
      type="text"
      name="username"
      value={formik.values.username}
      onChange={formik.handleChange}
     />
     {/* 没有验证通过的提示信息 */}
    <p>{formik.errors.username?formik.errors.username:null}</p> 
    <input
      type="password" 
      name="password"
      value={formik.values.password}
      onChange={formik.handleChange}
      />
      {/* 没有验证通过的提示信息 */}
    <p>{formik.errors.password?formik.errors.password:null}</p>
    
    <input type="submit" />
  </form>
  )
}


export default App

初始验证方式优化

  1. 以上代码虽能完成验证功能,但用户体验并不好,当将默认值置空时,initialValues: { username: ‘’, password: ‘’ },在表单输入用户名,会发现密码的验证提示显示。 所以需要进一步优化以提高用户体验。
  2. 开启离开焦点事件触发验证 onBlur={formik.handleBlur}
  3. 判断当前的表单数据是否被更改formik.touched
  4. 两者结合使用,缺一不可
import React from "react";
import { useFormik } from 'formik'

function App() {
  const formik = useFormik({
    initialValues:{username:"",password:""},
    validate:values=>{
      const errors={}
      if(!values.username){
        errors.username='请输入用户名'
      }else if(values.username>15){
        errors.username='用户名的长度不能大于15'
      }
      if(values.password<6) {
        errors.password='密码的长度不能小于6'
      }
      return errors
    },
    onSubmit:(values)=>{
      console.log(values) // 获取最新数据
    }
  })
  return (
  <form onSubmit={formik.handleSubmit}>
  {/* onBlur={formik.handleBlur}  离开焦点 */}
    <input
      type="text"
      name="username"
      value={formik.values.username}
      onChange={formik.handleChange}
      onBlur={formik.handleBlur} 
     />
     {/* 没有验证通过的提示信息 */}
     {/* 离开焦点之后要formik.touched 离开焦点和formik.touched缺一不可*/}
     {/* 先看数据有没有被更改 如果被更改在看看有没有验证通过 如果没有验证通过显示 formik.errors.username 否则什么都不显示*/}
    <p>{formik.touched.username&&formik.errors.username?formik.errors.username:null}</p> 
    <input
      type="password" 
      name="password"
      value={formik.values.password}
      onChange={formik.handleChange}
      onBlur={formik.handleBlur}
      />
      {/* 没有验证通过的提示信息 */}
      {/* 先看数据有没有被更改 如果被更改在看看有没有验证通过 如果没有验证通过显示 formik.errors.username 否则什么都不显示*/}
    <p>{formik.touched.username&&formik.errors.password?formik.errors.password:null}</p>
    
    <input type="submit" />
  </form>
  )
}


export default App

使用yup结合useFormik使用

import React from "react";
import { useFormik } from 'formik'
import * as Yup from 'yup'

function App() {
  const formik = useFormik({
    initialValues:{username:"",password:""},
    // validate:values=>{
    //   const errors={}
    //   if(!values.username){
    //     errors.username='请输入用户名'
    //   }else if(values.username>15){
    //     errors.username='用户名的长度不能大于15'
    //   }
    //   if(values.password<6) {
    //     errors.password='密码的长度不能小于6'
    //   }
    //   return errors
    // },
    // 基于Yup 很显然代码少很多
    validationSchema:Yup.object({
      username: Yup.string() // 定义username类型必须是字符串
          .max(15, '用户名的长度不能大于15')
          .required('请输入用户名'),
      password: Yup.string()// 定义password类型必须是字符串
          .max(20, '密码的长度不能大于20')
          .required('请输入密码')
  }),
    onSubmit:(values)=>{
      console.log(values) // 获取最新数据
    }
  })
  return (
  <form onSubmit={formik.handleSubmit}>
  {/* onBlur={formik.handleBlur}  离开焦点 */}
    <input
      type="text"
      name="username"
      value={formik.values.username}
      onChange={formik.handleChange}
      onBlur={formik.handleBlur} 
     />
     {/* 没有验证通过的提示信息 */}
     {/* 离开焦点之后要formik.touched 离开焦点和formik.touched缺一不可*/}
     {/* 先看数据有没有被更改 如果被更改在看看有没有验证通过 如果没有验证通过显示 formik.errors.username 否则什么都不显示*/}
    <p>{formik.touched.username&&formik.errors.username?formik.errors.username:null}</p> 
    <input
      type="password" 
      name="password"
      value={formik.values.password}
      onChange={formik.handleChange}
      onBlur={formik.handleBlur}
      />
      {/* 没有验证通过的提示信息 */}
      {/* 先看数据有没有被更改 如果被更改在看看有没有验证通过 如果没有验证通过显示 formik.errors.username 否则什么都不显示*/}
    <p>{formik.touched.username&&formik.errors.password?formik.errors.password:null}</p>
    
    <input type="submit" />
  </form>
  )
}


export default App

减少样板代码

img

import React from "react";
import { useFormik } from 'formik'
import * as Yup from 'yup'

function App() {
  const formik = useFormik({
    initialValues: { username: "", password: "" },
    // 基于Yup 很显然代码少很多
    validationSchema: Yup.object({
      username: Yup.string() // 定义username类型必须是字符串
        .max(15, '用户名的长度不能大于15')
        .required('请输入用户名'),
      password: Yup.string()// 定义password类型必须是字符串
        .max(20, '密码的长度不能大于20')
        .required('请输入密码')
    }),
    onSubmit: (values) => {
      console.log(values) // 获取最新数据
    }
  })
  return (
    <form onSubmit={formik.handleSubmit}>
      <input
        type="text"
        name="username"
        // value={formik.values.username}
        // onChange={formik.handleChange}
        // onBlur={formik.handleBlur}
        {...formik.getFieldProps('username')}
      />
      <p>{formik.touched.username && formik.errors.username ? formik.errors.username : null}</p>
      <input
        type="password"
        name="password"
        // value={formik.values.password}
        // onChange={formik.handleChange}
        // onBlur={formik.handleBlur}
        {...formik.getFieldProps('password')}
      />
      <p>{formik.touched.password && formik.errors.password ? formik.errors.password : null}</p>
      <input type="submit" />
    </form>
  )
}


export default App

使用组件的方式构建表单

组件内封装好的组件

  • Formik 表单最外层,可传递数据

    • initialValue 表单默认数据
    • onSubmit 表单提交行为
    • validationSchema 表单验证规则
  • Form 表单组件

  • Field 具体表单项

  • ErrorMessage 表单项提示信息显示

import React from "react";
import { Formik, Form, Field, ErrorMessage } from 'formik';
import * as Yup from 'yup';

function App() {
  const initialValues = {
    username: '' // 默认信息
  }
  const handleSubmit = (values) => {
    console.log(values) 
  }
  const Schema = Yup.object({
    username: Yup.string()
      .max(12, '用户名长度不能大于12')
      .required('请输入用户名')
  })
  return <Formik 
    initialValues={initialValues}
    onSubmit={handleSubmit}
    validationSchema={Schema}
  >
    <Form>
      <Field name="username"></Field>
      <ErrorMessage name="username"></ErrorMessage>
      <input type='submit' />
    </Form>
  </Formik>
}
export default App

构建其他表单项

  • 默认情况下,Filed组件渲染的是文本框, 若要生成其他表单元素可以使用如下语法
  • as 指定类型

img

import React from "react";
import { Formik, Form, Field, ErrorMessage } from 'formik';
import * as Yup from 'yup';

function App() {
  const initialValues = {
    username: '', // 默认信息
    content:"我是默认信息",
    subject:"java" // 默认信息
  }
  const handleSubmit = (values) => {
    console.log(values)
  }
  const Schema = Yup.object({
    username: Yup.string()
      .max(12, '用户名长度不能大于12')
      .required('请输入用户名')
  })
  return <Formik
    initialValues={initialValues}
    onSubmit={handleSubmit}
    validationSchema={Schema}
  >
    <Form>
      <Field name="username"></Field>
      <Field name="content" as="textarea"></Field>
      <Field name="subject" as="select">
        <option value='前端'>前端</option>
        <option value='java'>java</option>
      </Field>

      <ErrorMessage name="username"></ErrorMessage>
      <input type='submit' />
    </Form>
  </Formik>
}
export default App

构建自定义表单控件

  • formik提供的表单控件不能构建密码框、复选框这些,需要自定义
  • 使用 useFiled 去构建自定义表单控件

img

import React from "react";
import { Formik, Form, Field, useField } from 'formik';
import * as Yup from 'yup';

function MyInput({ label, ...props }) {
  // filed 表单属性  meta存放验证信息
  const [field, meta] = useField(props)
  // console.log(field,'field')
  // console.log(meta,'meta')
  // 构建视图
  return <div>
    <label htmlFor={props.id} >{label}</label>
    <input {...field} {...props} />
    {/* 判断值有没有被修改过(meta.touched)并且验证meta.error是否为真 如果是就是知道验证没有通过*/}
    {meta.touched && meta.error ? <span>{meta.error}</span> : null}
  </div>
}

function App() {
  const initialValues = {
    username: '', // 默认信息
  }
  const handleSubmit = (values) => {
    console.log(values)
  }
  const Schema = Yup.object({
    username: Yup.string()
      .max(12, '用户名长度不能大于12')
      .required('请输入用户名'),
    password: Yup.string()
      .min(6, '密码验证没有通过')
      .required('请输入密码'),
  })
  return <Formik
    initialValues={initialValues}
    onSubmit={handleSubmit}
    validationSchema={Schema}
  >
    <Form>
      <Field name="username"></Field>
      <MyInput id="myPass" label="密码" name="password" type="password" placeholder="请输入密码" />
      <input type='submit' />
    </Form>
  </Formik>
}
export default App

自定义复选框控件

需要先验证通过输入框

import React from "react";
import { Formik, Form, Field, useField } from 'formik';
import * as Yup from 'yup';

function Checkbox({ label, ...props }) {
  // helper 对象中 存方法
  const [field, meta, helper] = useField(props);
  const { value } = meta;
  const { setValue } = 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 htmlFor={props.id} >
      <input
        // 默认选中 
        checked={value.includes(props.value)}
        type="checkbox"
        {...props}
        onChange={handleChange} />
      {label}
    </label>
  </div>
}

function App() {
  const initialValues = {
    username: '', // 默认信息
    hobbies:[] // 初始值
  }
  const handleSubmit = (values) => {
    console.log(values)
  }
  const Schema = Yup.object({
    username: Yup.string()
      .max(12, '用户名长度不能大于12')
      .required('请输入用户名'),
  })
  return <Formik
    initialValues={initialValues}
    onSubmit={handleSubmit}
    validationSchema={Schema}
  >
    <Form>
      <Field name="username"></Field>
      <Checkbox value="足球" label="足球" name="hobbies" />
      <Checkbox value="篮球" label="篮球" name="hobbies" />
      <Checkbox value="橄榄球" label="橄榄球" name="hobbies" />
      
      <input type='submit' />
    </Form>
  </Formik>
}
export default App

受控组件和非受控组件区别

非受控组件

表单数据由DOM节点管理,特点是表单数据在需要时进行获取,代码实现相对简单

img

受控组件

表单数据由state对象管理,特点是可以实时得到表单数据,代码相对复杂

img

选⽤标准

总结: 受控组件和⾮受控组件都有其特点, 应该根据需求场进⾏选择. 在⼤多数情况下, 推荐使⽤受控组件处理表单数据. 如果表单在数据交互⽅⾯⽐简单, 使⽤⾮受控表单, 否则使⽤受控表单.

img

tisp:不过建议还是选择受控组件