React form 表单验证终极教程

A kitten
stone
前端工程师
最近更新 2022年05月12日

React form 表单验证终极教程

React form 表单是用户与网站 / app 重要的交互方式。我们需要在用户提交表单前对 form 表单进行验证,以确保用户填写了必选项以及在本地先过滤掉错误的数据,避免这些错误数据传入,减少服务器开销,提升用户体验。

本文将手把手教你通过 React + react-hook-form 异步表单验证插件来完成 React form 表单验证功能。

react-hook-form 这个工具库非常轻量,压缩后只有8.6KB,而且其内部通过 ref 来实现非受控组件,跟通过状态管理实现的表单组件不同,这种方法可以减少表单组件内的渲染次数。react-hook-form 暴露了6个自定义 hook API,并且也提供了强大的类型支持,可以让开发者以很低的成本接入。通过本教程学习,你可以了解到动态表单,嵌套表单,异步等多个常见的业务场景的使用方法,以及如何和主流UI框架进行集成。

一. 如何在 React 上安装 form 表单插件

通过 create-react-app 初始化项目


yarn create react-app kalacloud-form

然后安装 react-hook-form,这里推荐使用 yarn

npm install react-hook-form
// OR
yarn add react-hook-form

二. React form 表单基本使用

我们先从一个最基本的 form 表单开始,表单里包含 3 个输入项:姓名、电话、邮件。使用 useForm 这个 hook,可以导出 registerhandleSubmit 这两个方法。register 方法用来注册输入项字段,可以开启对输入项的验证,handleSubmit 用来提交表单,并触发表单的验证逻辑。

src 目录下新建文件夹 examples/basic-form,创建 index.js 文件 和 index.css 文件

// src/examples/basic-form/index.js
import { useForm } from "react-hook-form";
import "./index.css";

function BasicForm() {
  const { register, handleSubmit } = useForm();
  const onSubmit = (data) => {
    alert(JSON.stringify(data));
  };

  return (
    <div className="App">
      <h2>react-hook-form 基本使用方法</h2>
      <h3>「卡拉云 - 极速搭建企业内部工具,十倍提升开发效率」 </h3>
      <form onSubmit={handleSubmit(onSubmit)}>
        <div>
          <label htmlFor="name">姓名</label>
          <input {...register("name")} />
        </div>

        <div>
          <label htmlFor="phone">电话</label>
          <input {...register("phone")} />
        </div>

        <div>
          <label htmlFor="email">邮件</label>
          <input type="email" {...register("email")} />
        </div>
        <input type="submit" />
      </form>
    </div>
  );
}

export default BasicForm;

增加全局表单样式,修改 /src/App.css 文件如下

form {
  max-width: 500px;
  margin: 0 auto;
}

.form {
  background: #0e101c;
  max-width: 400px;
  margin: 0 auto;
}

p {
  color: #bf1650;
}

p::before {
  display: inline;
  content: "⚠ ";
}

input {
  display: block;
  box-sizing: border-box;
  width: 100%;
  border-radius: 4px;
  border: 1px solid #333;
  padding: 10px 15px;
  margin-bottom: 10px;
  font-size: 14px;
}

label {
  line-height: 2;
  text-align: left;
  display: block;
  margin-bottom: 13px;
  margin-top: 20px;
  color: #333;
  font-size: 14px;
  font-weight: 200;
}

input[type="submit"] {
  background: #547bec;
  color: #fff;
  text-transform: uppercase;
  border: none;
  margin-top: 40px;
  padding: 20px;
  font-size: 16px;
  font-weight: 100;
  letter-spacing: 10px;
}

input[type="submit"]:hover {
  background: #547bec;
}

input[type="button"]:active,
input[type="submit"]:active {
  transition: 0.3s all;
  transform: translateY(3px);
  border: 1px solid transparent;
  opacity: 0.8;
}

input:disabled {
  opacity: 0.4;
}

input[type="button"]:hover {
  transition: 0.3s all;
}

input[type="button"],
input[type="submit"] {
  -webkit-appearance: none;
}

.App {
  max-width: 600px;
  margin: 0 auto;
}

修改 /src/App.js 文件如下,并导入样式:

import BasicForm from "./examples/basic-form";
import "./App.css";

export default function App() {
  return <BasicForm />;
}

效果如下: kalacloud-卡拉云-基本使用

上面实现一个非常简单基础的表单功能,在日常开发中,对表单进行验证是必不可少的,下面为大家讲解 react-hook-form 的基础验证和自定义验证是如何实现的。

三. form 表单基础验证

<input
  type="text"
  {...register("First name", { required: true, maxLength: 80 })}
/>

register 方法的第二个参数接受一个对象,表示对当前表单项的验证规则。required 代表必填,maxLength 代表最多只能输入的字符个数。此外,register 还可以指定其他规则,比如 minLengthmaxminpattern 等,这些都是HTML5原生支持的验证规则。

自定义验证

自定义验证依然需要用到 register 这方法的第二个参数,它可以接受一个 validate 作为key,validate 可以是一个函数也可以是一个对象,下面看具体实例:

import { useForm } from "react-hook-form";

function CustomValidationForm() {
  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm();
  const onSubmit = (data) => {
    alert(JSON.stringify(data));
  };
  const intialValues = {
    firstName: "test",
    email: "admin@kalacloud.com",
    age: -1,
  };

  return (
    <div>
      <h1>react-hook-form 异步使用方法</h1>
      <h2>「卡拉云 - 极速搭建企业内部工具,十倍提升开发效率」 </h2>
      <form onSubmit={handleSubmit(onSubmit)}>
        <label htmlFor="firstName">姓名</label>
        <input
          defaultValue={intialValues.firstName}
          {...register("firstName", {
            validate: (value) => value === "kalacloud",
          })}
        />
        {errors.firstName && <p>你的名字不是kalacloud</p>}

        <label htmlFor="email">Email</label>
        <input
          defaultValue={intialValues.email}
          placeholder="someone@kalacloud.com"
          type="email"
          {...register("email")}
        />
        <label htmlFor="age">Age</label>
        <input
          defaultValue={intialValues.age}
          placeholder="0"
          type="text"
          {...register("age", {
            validate: {
              positiveNumber: (value) => parseFloat(value) > 0,
              lessThanHundred: (value) => parseFloat(value) < 20,
            },
          })}
        />
        {errors.age && errors.age.type === "positiveNumber" && (
          <p>你的年龄是非法的</p>
        )}
        {errors.age && errors.age.type === "lessThanHundred" && (
          <p>你的年龄应该小于20</p>
        )}

        <input type="submit" value="提交" />
      </form>
    </div>
  );
}

export default CustomValidationForm;

效果如下: kalacloud-卡拉云-自定义验证

自定义验证应是开发中最常见的情况,每个表单项都有各自复杂的验证规则,通过使用 validate 函数的时候,可以结合实际业务场景,定义灵活的验证规则。如果表单项实在太多,推荐使用 卡拉云 ,无需处理繁琐的前端问题,拖拽生成表单组件,轻松配置验证项,一键连接数据库,快速搭建企业内部后台管理工具。

不想处理繁琐的前端问题?

试用卡拉云,拖拽组件连接 API 或数据库直接生成后台系统,将两个月的工期降低至两天

另外,useForm 还接受一个对象座位可选参数,下面是每个字段的含义:

useForm({
  // 提交表单时的验证策略
  mode: 'onSubmit', // onChange | onBlur | onSubmit | onTouched | all = ‘onSubmit’
  //提交表单后,重新验证有错误的输入时的验证策略
  reValidateMode: 'onChange', // onChange | onBlur | onSubmit = 'onChange' 
  // 表单初始值
  defaultValues: {},
  // 表单上下文对象,将被注入到的 resolver 第二个参数
  context: undefined,
  // 此功能允许您使用任何外部验证库
  resolver: undefined,
  // 当设置为 firstError 时,只会收集每个字段的第一个错误
  criteriaMode: "firstError", // firstError | all
  // 当设置为 true 并且表单验证失败时,会将焦点设置在第一个出现错误的字段上
  shouldFocusError: true,
  // 组件卸载的时候是否保留其值
  shouldUnregister: false,
  // 是否开启浏览器原生表单验证功能
  shouldUseNativeValidation: false,
  // 此配置将延迟错误状态以毫秒为单位显示给最终用户
  delayError: undefined
})

更多 API 使用方法,可以参考 react-hook-form-官方文档

四. React form 动态表单

动态表单在实际业务开发中是非常常见的场景,这里可以通过 react-hook-form 轻松实现。

function DynamicForm() {
  const { register, handleSubmit, control, watch } = useForm();
  const { fields, append } = useFieldArray({
    control,
    name: "fieldArray",
  });
  const onSubmit = (data) => alert(JSON.stringify(data));

  const watchFieldArray = watch("fieldArray");
  const controlledFields = fields.map((field, index) => {
    return {
      ...field,
      ...watchFieldArray[index],
    };
  });

  return (
    <div className="dynamic-form">
      <form onSubmit={handleSubmit(onSubmit)}>
        <input {...register("title")} placeholder="标题" />

        {controlledFields.map((field, index) => {
          return <input {...register(`fieldArray.${index}.name`)} />;
        })}

        <button
          type="button"
          onClick={() =>
            append({
              name: "十倍提升开发效率",
            })
          }
        >
          Append
        </button>

        <input type="submit" />
      </form>
    </div>
  );
}

使用 useFieldArray hook 来实现动态字段的添加,并实时跟表单数据关联,效果如下

kalacloud-卡拉云-动态表单

动态表单在实际业务场景中也是非常常见的需求,通过增加,减少来控制表单项,react-hook-form 内部通过维护一个数组,来实现对数组表单项的可控制。

五. React form 嵌套表单

嵌套表单一般适用于组合表单的场景,或者需要对表单进行二次封装。

function Section1({ register }) {
  return (
    <>
      <div>
        <label>姓名</label>
        <input
          type="text"
          {...register("name", { required: true, maxLength: 80 })}
        />
      </div>
      <div>
        <label>年龄</label>
        <input
          type="number"
          {...register("age", { required: true })}
        />
      </div>
    </>
  );
}

function Section2({ register }) {
  return (
    <>
      <div>
        <label>邮箱</label>
        <input
          type="text"
          {...register("email", {
            required: true,
            pattern:
              /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
          })}
        />
      </div>
      <div>
        <label>手机号</label>
        <input
          type="tel"
          {...register("phone", {
            required: true,
            maxLength: 11,
            minLength: 8,
          })}
        />
      </div>
    </>
  );
}

function NestedForm() {
  const { register, handleSubmit } = useForm();
  const onSubmit = (data) => {
    alert(JSON.stringify(data));
  };

  return (
    <div className="nested-form">
      <form onSubmit={handleSubmit(onSubmit)}>
        <FormSection1 register={register} />
        <FormSection2 register={register} />
        <input type="submit" value={"提交"} />
      </form>
    </div>
  );
}

效果如下 kalacloud-卡拉云-嵌套表单

嵌套表单更多的是用于表单组件的封装,一些常用的表单项可以单独封装成一个独立的组件,复用在其他组件中,这里的核心要点就是透传 register 来实现这个功能,register 是跟表单实例进行绑定的,当表单提交的时候,可以使用 register 对子表单进行验证。

六. React form 异步表单

用户在页面上填写的表单,绝大部分场景下,都是需要提交到服务器的,这里可能就会涉及到跟服务端异步通信的过程,需要服务端对表单数据进行合法性校验。

const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

function AsyncForm() {
  const {
    register,
    handleSubmit,
    formState: { errors, isSubmitting },
  } = useForm();
  const onSubmit = async (data) => {
    await sleep(2000);
    if (data.username === "kalacloud") {
      alert(JSON.stringify(data));
    } else {
      alert("用户名必须是 kalacloud");
    }
  };

  return (
    <div>
      <h1>react-hook-form 异步使用方法</h1>
      <h2>「卡拉云 - 极速搭建企业内部工具,十倍提升开发效率」 </h2>
      <form onSubmit={handleSubmit(onSubmit)}>
        <label htmlFor="username">用户名</label>
        <input {...register("username")} />

        <label htmlFor="lastName">密码</label>
        <input type="password" {...register("lastName")} />

        <label htmlFor="email">邮箱</label>
        <input
          placeholder="someone@kalacloud.com"
          type="text"
          {...register("email")}
        />

        <div style={{ color: "red" }}>
          {Object.keys(errors).length > 0 && "发生了一些错误."}
        </div>

        <input type="submit" value={isSubmitting ? "正在提交..." : "提交"} />
      </form>
    </div>
  );
}

handleSubmit 允许接受一个异步函数,用来实现服务端异步校验,效果如下 kalacloud-卡拉云-异步表单验证

对验证规则比较苛刻的场景下,可能就需要用到服务端验证,这时候就可以用到 react-hook-form 的异步验证功能,关键点在于 handleSubmit 可以接受一个 Promise 作为提交函数。

七. 在 React UI 框架中集成

ant-design 是国内 React 生态中主流的 UI 框架,虽然其内部表单组件也提供了类似的 useForm 方法,但是正如前面所说,react-hook-form 相比较其他的UI框架有一定的性能优势,尤其是在表单数量且复杂的场景下,会有明显的性能优势,所以我们依然可以借助 react-hook-form 来进行二次封装。

先安装 ant-design

npm install antd --save
// OR
yarn add antd

在入口中引入样式文件

import "antd/dist/antd.min.css" // import "antd/dist/antd.css" 这种写法可能会有一些兼容性warning

使用 react-hook-form 封装 DatePicker, Select 组件

import { useForm, Controller } from "react-hook-form";
import { DatePicker, Button, Select } from "antd";

const { Option } = Select;

function WithAntdForm() {
  const { control, handleSubmit } = useForm();
  const onSubmit = (data) => alert(JSON.stringify(data));

  return (
    <div style={{ padding: "20px" }}>
      <form onSubmit={handleSubmit(onSubmit)}>
        <div>
          <Controller
            control={control}
            name="antdDatepicker"
            render={({ field }) => (
              <DatePicker
                {...field}
                style={{ width: "400px" }}
                onChange={(e) => {
                  field.onChange(e);
                  console.log(e["_d"]);
                }}
              />
            )}
            rules={{ required: true }}
          />
        </div>

        <div style={{ margin: "20px 0" }}>
          <Controller
            control={control}
            name="antdSelect"
            render={({ field }) => (
              <>
                <Select defaultValue="卡拉云" style={{ width: "400px" }} {...field}>
                  <Option value="极速搭建业务系统">极速搭建业务系统</Option>
                  <Option value="数十倍提升开发效率">数十倍提升开发效率</Option>
                  <Option value="拖拽构建,所见即所得">拖拽构建,所见即所得</Option>
                  <Option value="连接所有内部、外部数据">连接所有内部、外部数据</Option>
                </Select>
              </>
            )}
          />
        </div>
        <Button type="primary" htmlType="submit" style={{ width: "400px" }}>Submit</Button>
      </form>
    </div>
  );
}

效果如下 kalacloud-卡拉云-异步表单验证

react-hook-form 的表单功能十分强大并且性能优秀,如果有表单数据十分庞大的场景,可以在 antd 中集成 react-hook-form 来配合使用。

本文中所有代码都可以在 我们的 github上找到。

八. React form 表单验证总结

本文我们学习 react-hook-form 表单嵌套,异步验证等多种使用方法,足以应付日常的开发需求,但是往往现实并不是那么美好,在实际业务开发过程中,往往表单的数量是极其庞大的,字段数量多且繁杂,这个时候不免会带来巨大的工作量。如果不想处理前端问题,推荐使用卡拉云,卡拉云内置包括表单验证插件在内的各类组件,无需懂任何前端,仅需拖拽即可快速生成。 kalacloud-卡拉云 卡拉云内置表单组件,拖拽即用,填空配置表单校验,轻松完成表单验证。

下面是用卡拉云搭建的数据库 CURD 后台管理系统,只需拖拽组件,即可在10分钟内完成搭建。查看 3 分钟上手视频

卡拉云 SQL admin 后台管理系统
卡拉云是新一代低代码开发平台,与前端框架 Vue、React等相比,卡拉云的优势在于不用首先搭建开发环境,直接注册即可开始使用。开发者完全不用处理任何前端问题,只需简单拖拽,即可快速生成所需组件,可一键接入包括 MySQL 在内的常见数据库及 API,根据引导简单几步打通前后端,数周的开发时间,缩短至 1 小时。立即免费[试用卡拉云](https://my.kalacloud.com/signup)。