概要
大家好,我是Alex独开。 在本文中,我们总结了如何使用 React Hook Form 和 Yup 实现输入表单的验证。 通过结合 React Hook Form 和 Yup,即使表单中的输入项很多或验证项很复杂,也可以非常方便高效地实现。 如果你正在考虑使用 React Hook Form 和 Yup,或者你已经阅读了官方文档但不知道如何使用它,请参考这篇文章。
开发环境
-
Windows10+
-
Next.js 14.0.4
-
React 19
-
Sass 1.83.7
-
React Hook Form 7.54.2
-
@hookform/resolvers 3.9.1
-
Yup 1.6.1
-
Visual Studio Code 1.84.1
前提
这里假设你已经使用 Next.js 创建完成了项目 在这里我们不会讨论如何使用 Next.js构建环境。
安装必要的库
在您创建的 Next.js 项目中,需要添加以下三个库
-
React Hook Form
-
Yup
-
@hookform/resolvers
安装的执行命令,请参阅以下内容。
//TERMINAL
npm install react-hook-form yup @hookform/resolvers
接下来我们先简单介绍一下每个库。
-
React Hook Form React Hook Form 是一个基于React Hooks的表单库,旨在通过提供一系列的钩子(Hooks)来简化表单状态管理和验证。它具有高性能、轻量级、易于扩展和校验的特点,能够显著减少代码量,提高代码的可读性和可维护性。
-
Yup Yup是一个功能强大且易用的JavaScript对象模式验证库,主要用于数据验证。它以声明式的方式对数据结构进行验证,广泛应用于React、Vue等前端框架以及其他JavaScript应用中。 虽然我们也可以使用React Hook Form 实现表单的验证,但是Yup允许我们设置各种验证规则,例如必须输入检查和正则表达式检查。
-
@hookform/resolvers React Hook Form与外部验证库Yup结合使用的时候必须使用@hookform/resolvers作为纽带
使用 Yup 实现表单验证
首先,使用Yup 实现表单验证 先把表单验证的所有代码都复制出来
//src/components/form/validation.tsx
import * as yup from 'yup';
const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]{8,}$/;
export const schema = yup.object().shape({
fullName: yup.string().required("请输入名称"),
email: yup.string().email("邮箱格式不正确").required("请输入邮箱"),
password: yup.string().required("请输入密码").min(8).matches(passwordRegex, "密码包含非法字符"),
confirmPassword: yup.string().required("请再次输入密码").oneOf([yup.ref('password'),], '两次密码输入不一致'),
});
- 首先,我们使用 object() 将输入表单的输入值定义为对象。 接下来,使用 shape() 指定具体的数据格式要求。
- 使用 required() ,则该字段为必填字段。
- 使用 email() ,则该字段必须采用电子邮件地址的形式。
- 使用 matches() , 则该字段必须符合您自己定义的正则表达式。
- 使用 oneOf() , 则该字段必须与您指定项目的数值匹配。 在本例中,我们结合Yup.ref()使用,以验证它是否与密码的值匹配。
此外,您还可以使用 min() 和 max() 限制输入的字符数。 这里不对所有的验证方法进行一一介绍,大家请参阅 Yup 的 GitHub查看详细内容。
用户注册界面的实现
//\app\components\input\input.module.scss
.formWrapper {
display: flex;
flex-direction: column;
text-align: center;
margin:0 auto;
padding-left: 10%;
padding-right: 10%;
}
.formItemWrapper{
display: flex;
flex-direction: column;
text-align: start;
padding: 24px 0;
}
.formErrorMessage{
text-align: left;
color: red;
}
//\app\page.tsx
"use client";
import { yupResolver } from "@hookform/resolvers/yup";
import yup from "yup";
import { useForm, SubmitHandler } from "react-hook-form";
import { schema } from "./validation";
import styles from "./form.module.scss";
// 1
type ContactFormData = yup.InferType<typeof schema>;
export const Form = () => {
// 2
const { register, handleSubmit, formState } = useForm<ContactFormData>({
resolver: yupResolver(schema), // 结合Yup
mode: "onBlur", // 指定验证表单的时机
});
const onSubmit: SubmitHandler<ContactFormData> = (data) => {
// 表单检证成功,可以进行后续处理
console.log(data);
};
// 3
return (
<form className={styles.formWrapper} onSubmit={handleSubmit(onSubmit)}>
<h1>
用户注册
</h1>
<div>
请输入用户注册信息
</div>
<div className={styles.formItemWrapper}>
<label>姓名</label>
<input {...register("fullName")} />
<span className={styles.formErrorMessage}>{formState.errors.fullName?.message}</span>
</div>
<div className={styles.formItemWrapper}>
<label>邮箱地址</label>
<input {...register("email")} />
<span className={styles.formErrorMessage}>{formState.errors.email?.message}</span>
</div>
<div className={styles.formItemWrapper}>
<label>密码</label>
<input type="password" autoComplete="false" {...register("password")} />
<span className={styles.formErrorMessage}>{formState.errors.password?.message}</span>
</div>
<div className={styles.formItemWrapper}>
<label>密码(确认)</label>
<input type="password" autoComplete="false" {...register("confirmPassword")} />
<span className={styles.formErrorMessage}>{formState.errors.confirmPassword?.message}</span>
</div>
<div className={styles.buttonContainer}>
<button disabled={!formState.isValid}>注册</button>
</div>
</form>
);
};
至于 CSS,它与这个 input 表单验证的实现没有直接关系,所以实现保持在最低限度。 仅供大家参照。
我将按顺序解释 form.tsx 的代码。
- 通过使用yup.InferType,依据validation.tsx中定义的schema创建一个type类型的对象。
- 使用useForm定义表单的验证内容
- 参数resolver中 yupResolver 设置为参数解析器,实现与 Yup 的联动
- mode 参数中控制验证检查的时间。 在此示例中,设置了 onBlur,所以在输入字段失焦时执行验证检查。 有关其他设置,请参阅 useForm 文档。
- 使用useForm中的Register 用于关联每个输入框字段的相应验证。 {... register(TFieldName)} 表示法乍一看可能会让人感到困惑,但 useForm 文档描述了调用 registerAPI 时的具体方法。 这将帮助您更好地理解。
const { onChange, onBlur, name, ref } = register('firstName');
// include type check against field path with the name you have supplied.
<input
onChange={onChange} // assign onChange event
onBlur={onBlur} // assign onBlur event
name={name} // assign name prop
ref={ref} // assign ref prop
/>
// same as above
// 下面的一句与上面的代码等效
<input {...register('firstName')} />
此外,通过使用 formState用于向每个输入表单显示错误消息,并且在所有输入表单都验证通过检查之前,无法按下注册按钮。
通过以上的实现,将获取与下图相同的注册页面。
使用 React Hook Form 处理你的自定义组件
在实际项目中,你可能需要处理一个包含 的自制组件。 如果你按按照上面的方法定义到你自定义的组件,React Hook Form 也不会正确响应。 要解决这个问题,你需要使用 forwardRef 实现组件。
举一个具体的例子。 下面的代码实际上是与上述 src/components/form/form.tsx 分开的 。
//\app\components\input\input.module.scss
.formItemWrapper{
display: flex;
flex-direction: column;
text-align: start;
padding: 24px 0;
}
.formErrorMessage{
text-align: left;
color: red;
}
//\app\components\input\input.tsx
import { ComponentPropsWithRef, forwardRef } from "react";
import styles from "./input.module.scss";
export type InputProps = {
labelText : string,
errorMessage? : string,
} & ComponentPropsWithRef<"input">
export const Input = forwardRef<HTMLInputElement, InputProps>((
{
labelText,
errorMessage,
...props
}: InputProps,
ref
) => {
return (
<div className={styles.formItemWrapper}>
<label>{labelText}</label>
<input ref={ref} {...props}/>
<span className={styles.formErrorMessage}>{errorMessage}</span>
</div>
);
});
Input.displayName = 'Input';
//\app\page.tsx
"use client";
import { yupResolver } from "@hookform/resolvers/yup";
import yup from "yup";
import { useForm, SubmitHandler } from "react-hook-form";
import { schema } from "./validation";
import styles from "./form.module.scss";
import { Input } from "./components/input/input";
// 1
type ContactFormData = yup.InferType<typeof schema>;
const RegisterForm = () => {
// 2
const { register, handleSubmit, formState: { errors, isValid } } = useForm<ContactFormData>({
resolver: yupResolver(schema), // 结合Yup
mode: "onBlur", // 指定验证表单的时机
});
const onSubmit: SubmitHandler<ContactFormData> = (data) => {
// 表单检证成功,可以进行后续处理
console.log(data);
};
// 3
return (
<form className={styles.formWrapper} onSubmit={handleSubmit(onSubmit)}>
<h1>
用户注册
</h1>
<div>
请输入用户注册信息
</div>
<Input
type="text"
labelText="姓名"
errorMessage={errors.fullName?.message}
{...register("fullName")}
/>
<Input
type="text"
labelText="邮箱地址"
errorMessage={errors.email?.message}
{...register("email")}
/>
<Input
type="password"
autoComplete="new-password"
labelText="密码"
errorMessage={errors.password?.message}
{...register("password")}
/>
<Input
type="password"
autoComplete="new-password"
labelText="密码(确认)"
errorMessage={errors.confirmPassword?.message}
{...register("confirmPassword")}
/>
<div className={styles.buttonContainer}>
<button disabled={!isValid}>注册</button>
</div>
</form>
);
};
export default RegisterForm;
现在 register 可以正常访问自组建的ref了,子组件的异常检证可以正常处理了
小结
除了本文中介绍的那些之外,还可以通过结合 React Hook Form 和 Yup 来实现具有各种验证的输入表单。 我还在学习,但我认为将来会有很多机会用输入表单实现东西,所以我想加深理解并提高开发速度。
参考站点
首页 | React Hook Form - 简单的 React 表单验证 www.react-hook-form.com/
react-hook-form/resolvers github.com/react-hook-…
React react.dev/