(笔记)formik -- 表单校验利器

01. formik介绍及安装

简介

  • formik是一个React库,用于构建强大且易于理解的表单处理逻辑。
  • 它减少了表单的样板代码量,并帮助处理表单的状态、校验和错误处理。

安装

  • 使用npm安装formik:npm install formik --save
  • 如使用yarn:yarn add formik

简单使用

jsx 复制代码
import { useFormik } from 'formik';

function App() {
    const formik = useFormik({
        initialValues: { username: '张三' },
        onSubmit: values => { }
    });

    return (
        <form onSubmit={formik.handleSubmit}>
            <input type="text" name="username"
                value={formik.values.username}
                onChange={formik.handleChange}
            />
            <input type="submit" />
        </form>
    );
}

formik和antd表单组件联合使用

jsx 复制代码
import React from 'react';
import { Formik } from 'formik';
import { Form, Input, Button, message } from 'antd';

const LoginForm = () => {
  return (
    <Formik
      initialValues={{ username: '', password: '' }}
      onSubmit={(values, actions) => {
        message.success(`登录成功:用户名 - ${values.username},密码 - ${values.password}`);
        actions.setSubmitting(false);
      }}
    >
      {({ values, handleChange, handleBlur, handleSubmit, isSubmitting }) => (
        <Form onFinish={handleSubmit}>
          <Form.Item label="用户名">
            <Input
              name="username"
              onChange={handleChange}
              onBlur={handleBlur}
              value={values.username}
            />
          </Form.Item>
          <Form.Item label="密码">
            <Input.Password
              name="password"
              onChange={handleChange}
              onBlur={handleBlur}
              value={values.password}
            />
          </Form.Item>
          <Form.Item>
            <Button type="primary" htmlType="submit" disabled={isSubmitting}>
              登录
            </Button>
          </Form.Item>
        </Form>
      )}
    </Formik>
  );
};

export default LoginForm;

02. formik基础讲解(一)

基本原则

  • formik采用React理念简化表单处理,它提供了<Formik />组件封装表单的状态和行为。
  • 支持受控组件和非受控组件。

使用formik

  • 使用<Formik />组件可以容易地创建一个表单,并通过其valueshandleSubmit属性管理表单状态和提交。

03. formik基础讲解(二)

表单字段

  • 通过<Field />组件来管理各个表单字段,如<Field type="text" name="firstName" />
  • formik自动连接<Field />到表单状态,无需手动管理onChange和value属性。

表单校验

  • formik可以在字段和表单级别进行校验,可以同步或异步返回错误对象。
  • 必须设置validate或validationSchema属性来告知formik如何进行校验。

下面是一个验证的示例:

jsx 复制代码
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.length > 15) {
        errors.username = '用户名的长度不能超过15';
      }
      
      if (values.password.length < 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}
        onBlur={formik.handleBlur}
      />
      {formik.touched.username && formik.errors.username ? (
        <div>{formik.errors.username}</div>
      ) : null}
      <input
        type="password"
        name="password"
        value={formik.values.password}
        onChange={formik.handleChange}
        onBlur={formik.handleBlur}
      />
      {formik.touched.password && formik.errors.password ? (
        <div>{formik.errors.password}</div>
      ) : null}
      <button type="submit">提交</button>
    </form>
  );
}

export default App;

表单校验的时机需要被指定为合适的时机,这样可以提高用户使用的体验感。下面就通过onBlur事件对其进行改善。

这里可以利用formik.touched这个对象,比如用户已经修改过username的值,那么formik.touched.username的值就不再为假了。

jsx 复制代码
onBlur={formik.handleBlur}

onBlur={formik.handleBlur}

<p>{formik.touched.username && formik.errors.username ? formik.errors.username : null}</p>

<p>{formik.touched.password && formik.errors.password ? formik.errors.password : null}</p>

上述的代码进行了两个方面的改善:1. 在表单失去焦点之后才进行校验; 2. 如果一个表单的内容从来没有被更改过,则不会对其进行校验。

04. formik结合yup进行表单验证

yup介绍

  • yup是一个构建对象模式的JavaScript模式验证器,用于验证和解析数据。
  • 它提供了一种声明式方法来创建校验模式。

验证方法

  • formik结合yup可以轻松实现表单验证:validationSchema={Yup.object({...})}
  • yup允许创建复杂的校验逻辑,如必填项、字符长度、正则表达式等。

使用yup可以有效减少表单验证过程中的代码量,并且使代码逻辑变得简洁。这得益于其对常见的表单验证规则的高度封装,在使用的时候我们只需要查询然后使用合适的规则即可。 首先安装yup: yarn add yup

jsx 复制代码
  import * as Yup from "yup";
  ...
  const schema = Yup.object({
    username: Yup.string()
      .max(15, "用户名的长度不能大于15")
      .required("请输入用户名"),
    password: Yup.string().min(6, '密码长度不能少于6位'),
  });
  
  validationSchema={schema}

使用validationSchema和yup搭配之后就不需要再使用validate配置了。

05. 使用getFieldProps方法简化代码

简化表单代码

  • getFieldProps是formik提供的一个实用方法,它封装了字段的value, onChange和onBlur事件。
  • 适用于快速实现常见的表单字段,可以显著减少重复代码量。

也就是说,形如下的代码可以被简化:

jsx 复制代码
      <input
        type="password"
        name="password"
        value={formik.values.password}
        onChange={formik.handleChange}
        onBlur={formik.handleBlur}
      />

简化之后的代码为:

jsx 复制代码
      <input
        type="password"
        name="password"
        {...formik.getFieldProps('password')}
      />

当然,如果你想要自定义onChange或者onBlur的回调,上述的做法显然就不合适了。

06. 使用组件的方式构建表单

组件化表单

  • formik支持构建和复用表单组件,通过<Formik />组件和Context API实现跨组件通信。
  • 可以创建包装<Field />的自定义组件,提高代码复用性和可维护性。

React组件

  • formik完全遵守React的组件化原则,可以和其他库或自定义逻辑无缝集成。
jsx 复制代码
// 引入React核心库
import React from "react"; 
// 引入Formik库相关的组件,Formik是用于构建表单的React库
import { Formik, Form, Field, ErrorMessage } from "formik"; 
// 引入Yup库,Yup是用于构建对象模式的JavaScript schema builder,可用于值的校验
import * as Yup from "yup";

// 定义App函数组件
function App() {
  // 设置表单的初始值
  const initialValues = {username: '', hobbies: ['足球', '篮球']};
  
  // 提交表单触发的函数,这里只是将表单的值打印在控制台
  const handleSubmit = (values) => {
    console.log(values);
  };
  
  // 使用Yup定义校验模式
  const schema = Yup.object({
    // 定义username字段为字符串类型,最大长度为15,并且是必填项
    username: Yup.string()
      .max(15, "用户名的长度不能大于15")
      .required("请输入用户名"),
  });
  
  // 渲染组件
  return (
    // Formik组件用来包裹整个表单,并提供表单管理的功能
    <Formik
      initialValues={initialValues} // 设置初始化值
      onSubmit={handleSubmit} // 提交表单执行的函数
      validationSchema={schema} // 设置表单校验的模式
    >
      {/* Form组件表示一个表单 */}
      <Form>
        {/* Field组件,对应一个input输入框,name属性指明它是formik管理 state中的哪一个字段  */}
        <Field name="username" />
        {/* ErrorMessage 组件用于显示与Field组件同名字段的错误信息 */}
        <ErrorMessage name="username" />
        {/* 提交按钮 */}
        <input type="submit"/>
      </Form>
    </Formik>
  );
}

// 将App组件导出,允许在其他文件中使用
export default App;

虽然使用组件的方式构造表单很方便,但是相应的自由度上收到了比较大的限制。

07. field组件的as属性

默认情况下,Field组件渲染的是文本框,如果想要生成其他表单元素可以采用as属性:

jsx 复制代码
<Field name="content" as="textarea" />
<Field name="subject" as="select">
  <option value="前端">前端</option>
  <option value="Java">Java</option>
  <option value="python">python</option>
</Field>

08. 自定义表单控件

显然Field组件即使采用了as属性作为其拓展的重要方式,但是还是会有很多不如意的地方,这个时候让开发者自行封装符合自己项目主题的表单控件就显得尤为重要了!

下面,就通过useField钩子函数封装一个表单控件。

jsx 复制代码
import { useField } from "formik";
function MyIpt ({label, ...props}) {
    const [field, meta] = useField(props);
    return (
        <div>
            <label htmlFor={props.id}>{label}</label>
            <input {...field} {...props} />
            {meta.touched && meta.error ? <div>{meta.error}</div> : null}
        </div>
    )
}

上述代码中的...field和...props实际上是为了给input标签上绑定了两种不同的数据,前者来自于formik而后者来自于调用方;

useField钩子函数的执行结果的返回值为field和meta,其中field中包含的是onChange onBlur value这些信息;而meta则包含的是和error相关的错误处理的信息。

下面是自定义组件的使用方式:

jsx 复制代码
<MyIpt label='密码' type='password' name='password' placeholder='请输入密码' id='ps' />

09. 封装一个Checkbox自定义标签

jsx 复制代码
// 定义一个名为Checkbox的函数组件,它接收一个label属性和其他props属性
function Checkbox ({label, ...props}) {
  // 使用Formik的useField钩子来获取一些有用的字段属性和方法
  const [field, meta, helper] = useField(props);

  // 从meta对象中结构出value属性,这将是当前checkbox组组的值
  const { value } = meta;

  // 从helper对象中结构出setValue方法,用于在需要时更新checkbox组的值
  const { setValue } = helper;

  // 定义handleChange函数,此函数将在checkbox的值发生改变时被调用
  const handleChange = () => {
    // 创建一个新的Set实例,它将包含当前的checkbox值
    const set = new Set(value);

    // 检查当前的checkbox项目是否已经选中,如果是,则删除它;如果没有,则添加它
    if (set.has(props.value)) {
      set.delete(props.value); // 如果set中已有相应的值,删除它
    }else {
      set.add(props.value); // 如果set中没有相应的值,添加它
    }

    // 使用setValue来更新checkbox组的值,将Set转为数组
    setValue([...set])
  }

  // 渲染checkbox组件,其中包括了一个input元素和它的标签label
  return (
    <div>
      <label htmlFor="">
        {/* input元素的类型设置为checkbox,checked属性决定了这个checkbox是否被选中 */}
        {/* 使用扩展运算符将props传递给input元素,同时为input设置onChange事件处理函数 */}
        <input checked={value.includes(props.value)} type="checkbox" {...props} onChange={handleChange}/> {label}
      </label>
    </div>
  );
}

上述这段代码定义了一个名为Checkbox的React函数组件,它使用了Formik库的useField钩子。useField钩子用于管理表单字段的状态和更新操作。Checkbox组件旨在处理一个复选框(checkbox)输入,并能够在Formik表单的上下文中正确地更新其值。通过Set来管理选中项的集合,可以有效地切换选中状态且保持选中项的值唯一。组件通过传入的label和props渲染一个复选框和其对应的标签。当复选框的选中状态发生变化时,handleChange函数将被调用,更新复选框的状态到Formik的状态管理中去。

使用上述自定义组件:

jsx 复制代码
    const initialValues = {username: '', hobbies: ['足球', '篮球']};
    <Formik
      initialValues={initialValues}
      onSubmit={handleSubmit}
      validationSchema={schema}
    >
      <Form>
        <Checkbox value="足球" label="足球" name="hobbies"/>
        <Checkbox value="篮球" label="篮球" name="hobbies"/>
        <Checkbox value="橄榄球" label="橄榄球" name="hobbies"/>
        <input type="submit"/>
      </Form>
    </Formik>

进阶用法指南

01. field组件与fieldArray的用法

field组件

  • <Field />组件支持各种HTML输入元素和自定义组件,便于扩展。

fieldArray组件

  • <FieldArray />组件用于管理动态表单数组,如列表添加、删除操作。

02. 构建高性能表单解决方案

表单性能问题

  • formik的性能挑战主要与大型表单和复杂结构相关。
  • 解决性能问题常常涉及减少不必要的渲染。

性能优化策略

  • 使用formik提供的优化工具,如React.memoshouldComponentUpdate
  • 将表单拆分为更小的组件,减少单一组件的复杂度。

03. 构建高性能表单解决方案进阶

进阶策略

  • 使用formik的<FastField />替代<Field />,针对个别字段进行性能优化。
  • 利用React的useCallbackuseMemo避免不必要的函数重建和计算。

实际案例分析

  • 分析真实世界中的表单应用,如电商网站的结账过程表单。
  • 探讨在不同场景下使用formik进行性能调优的具体情况。
相关推荐
m0_7482552619 分钟前
前端安全——敏感信息泄露
前端·安全
鑫~阳2 小时前
html + css 淘宝网实战
前端·css·html
Catherinemin2 小时前
CSS|14 z-index
前端·css
2401_882727573 小时前
低代码配置式组态软件-BY组态
前端·后端·物联网·低代码·前端框架
NoneCoder4 小时前
CSS系列(36)-- Containment详解
前端·css
anyup_前端梦工厂4 小时前
初始 ShellJS:一个 Node.js 命令行工具集合
前端·javascript·node.js
5hand4 小时前
Element-ui的使用教程 基于HBuilder X
前端·javascript·vue.js·elementui
GDAL4 小时前
vue3入门教程:ref能否完全替代reactive?
前端·javascript·vue.js
六卿4 小时前
react防止页面崩溃
前端·react.js·前端框架
z千鑫5 小时前
【前端】详解前端三大主流框架:React、Vue与Angular的比较与选择
前端·vue.js·react.js