如何使用react-hook-form+zod实现动态表单验证?
如何使用react-hook-form+zod实现动态表单验证?比如登陆时不需要输入邮箱,注册时需要输入邮箱
{variant === "Register" && (
<div className="space-y-2">
<label htmlFor="email" className="inline font-semibold">
邮箱
</label>
<input
id="email"
{...register("email", { shouldUnregister: true })}
type="text"
className="w-full rounded bg-gray-100 px-3 py-1.5 outline-none"
/>
{errors.email && (
<p className="text-sm text-rose-500">{errors.email.message}</p>
)}
</div>
)}
大概的样子:
以下是完整代码,无法实现对 email
项的校验
"use client";
import { useState } from "react";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import * as z from "zod";
const LoginFormSchema = z.object({
username: z
.string()
.min(3, {
message: "用户名不得少于3个字符",
})
.max(20, {
message: "用户名不得多于20个字符",
}),
password: z
.string()
.min(8, {
message: "密码不得少于8个字符",
})
.max(20, {
message: "密码不得多于20个字符",
}),
});
const RegisterFormSchema = z.object({
email: z.string().email({
message: "邮箱格式不正确",
}),
username: z
.string()
.min(3, {
message: "用户名不得少于3个字符",
})
.max(20, {
message: "用户名不得多于20个字符",
}),
password: z
.string()
.min(8, {
message: "密码不得少于8个字符",
})
.max(20, {
message: "密码不得多于20个字符",
}),
});
const FormSchema = z.union([LoginFormSchema, RegisterFormSchema]);
type FormSchemaType = z.infer<typeof FormSchema>;
const UserForm = () => {
const [variant, setVariant] = useState<"Login" | "Register">("Login");
const {
register,
handleSubmit,
formState: { errors, isSubmitting },
watch,
} = useForm<FormSchemaType>({
resolver: zodResolver(FormSchema),
defaultValues: {
username: "",
password: "",
},
});
const onSubmit = async (data: FormSchemaType) => {
await new Promise((resolve) => setTimeout(resolve, 1000));
};
return (
<div className="flex min-h-screen items-center justify-center bg-gray-100">
<div className="max-w-md flex-1">
<form
onSubmit={handleSubmit(onSubmit)}
className="space-y-4 rounded-lg bg-white p-6 shadow-xl"
>
<h2 className="text-center text-xl font-semibold">
{variant === "Login" ? "登录" : "注册"}
</h2>
{variant === "Register" && (
<div className="space-y-2">
<label htmlFor="email" className="inline font-semibold">
邮箱
</label>
<input
id="email"
{...register("email", { shouldUnregister: true })}
type="text"
className="w-full rounded bg-gray-100 px-3 py-1.5 outline-none"
/>
{errors.email && (
<p className="text-sm text-rose-500">{errors.email.message}</p>
)}
</div>
)}
<div className="space-y-2">
<label htmlFor="username" className="inline font-semibold">
用户名
</label>
<input
id="username"
{...register("username")}
type="text"
className="w-full rounded bg-gray-100 px-3 py-1.5 outline-none"
/>
{errors.username && (
<p className="text-sm text-rose-500">{errors.username.message}</p>
)}
</div>
<div className="space-y-2">
<label htmlFor="password" className="inline font-semibold">
密码
</label>
<input
id="password"
{...register("password")}
type="password"
className="w-full rounded bg-gray-100 px-3 py-1.5 outline-none"
/>
{errors.password && (
<p className="text-sm text-rose-500">{errors.password.message}</p>
)}
</div>
<p className="text-right text-sm">
{variant === "Login" ? "还没有账号?去" : "已有账号?去"}
<span
className="cursor-pointer font-semibold underline"
onClick={() =>
setVariant(variant === "Login" ? "Register" : "Login")
}
>
{variant === "Login" ? "注册" : "登录"}
</span>
</p>
<button
type="submit"
className="w-full rounded-md bg-zinc-900 px-4 py-2 text-sm text-white hover:bg-zinc-900/90 disabled:cursor-not-allowed disabled:bg-gray-500"
disabled={isSubmitting}
>
提交
</button>
</form>
<code>
<pre className="p-2">{JSON.stringify(watch(), null, 2)}</pre>
</code>
</div>
</div>
);
};
export default UserForm;
回复
1个回答

test
2024-06-27
在 schema email
中添加 optional()
选项
修改过后的完整代码:
"use client";
import { useState } from "react";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import * as z from "zod";
const FormSchema = z.object({
email: z
.string()
.email({
message: "邮箱格式不正确",
})
.optional(),
username: z
.string()
.min(3, {
message: "用户名不得少于3个字符",
})
.max(20, {
message: "用户名不得多于20个字符",
}),
password: z
.string()
.min(8, {
message: "密码不得少于8个字符",
})
.max(20, {
message: "密码不得多于20个字符",
}),
});
type FormSchemaType = z.infer<typeof FormSchema>;
const UserForm = () => {
const [variant, setVariant] = useState<"Login" | "Register">("Login");
const {
register,
handleSubmit,
formState: { errors, isSubmitting },
watch,
} = useForm<FormSchemaType>({
resolver: zodResolver(FormSchema),
});
const onSubmit = async (data: FormSchemaType) => {
await new Promise((resolve) => setTimeout(resolve, 1000));
};
return (
<div className="flex min-h-screen items-center justify-center bg-gray-100">
<div className="max-w-md flex-1">
<form
onSubmit={handleSubmit(onSubmit)}
className="space-y-4 rounded-lg bg-white p-6 shadow-xl"
>
<h2 className="text-center text-xl font-semibold">
{variant === "Login" ? "登录" : "注册"}
</h2>
{variant === "Register" && (
<div className="space-y-2">
<label htmlFor="email" className="inline font-semibold">
邮箱
</label>
<input
id="email"
{...register("email", { shouldUnregister: true })}
type="text"
className="w-full rounded bg-gray-100 px-3 py-1.5 outline-none"
/>
{errors.email && (
<p className="text-sm text-rose-500">{errors.email.message}</p>
)}
</div>
)}
<div className="space-y-2">
<label htmlFor="username" className="inline font-semibold">
用户名
</label>
<input
id="username"
{...register("username")}
type="text"
className="w-full rounded bg-gray-100 px-3 py-1.5 outline-none"
/>
{errors.username && (
<p className="text-sm text-rose-500">{errors.username.message}</p>
)}
</div>
<div className="space-y-2">
<label htmlFor="password" className="inline font-semibold">
密码
</label>
<input
id="password"
{...register("password")}
type="password"
className="w-full rounded bg-gray-100 px-3 py-1.5 outline-none"
/>
{errors.password && (
<p className="text-sm text-rose-500">{errors.password.message}</p>
)}
</div>
<p className="text-right text-sm">
{variant === "Login" ? "还没有账号?去" : "已有账号?去"}
<span
className="cursor-pointer font-semibold underline"
onClick={() =>
setVariant(variant === "Login" ? "Register" : "Login")
}
>
{variant === "Login" ? "注册" : "登录"}
</span>
</p>
<button
type="submit"
className="w-full rounded-md bg-zinc-900 px-4 py-2 text-sm text-white hover:bg-zinc-900/90 disabled:cursor-not-allowed disabled:bg-gray-500"
disabled={isSubmitting}
>
提交
</button>
</form>
<code>
<pre className="p-2">{JSON.stringify(watch(), null, 2)}</pre>
</code>
</div>
</div>
);
};
export default UserForm;
回复

适合作为回答的
- 经过验证的有效解决办法
- 自己的经验指引,对解决问题有帮助
- 遵循 Markdown 语法排版,代码语义正确
不该作为回答的
- 询问内容细节或回复楼层
- 与题目无关的内容
- “赞”“顶”“同问”“看手册”“解决了没”等毫无意义的内容