示例说明
表单状态管理最怕三处失同步:字段注册、校验规则、提交处理。zodResolver 把校验完全交给 schema:字段规则(长度、正则)与跨字段一致性(两次密码相同)都在一个声明里,Hook Form 只负责注册与状态;错误对象按字段名分组,渲染层直接按名取消息;提交回调的参数类型由 schema 推断。
技术要点
- schema 是校验规则的唯一来源
- refine 表达跨字段一致性约束
- isSubmitting 驱动提交按钮防抖
代码解读
SignupSchema 用链式约束表达密码强度并 refine 校验两次输入一致,path 指定错误挂到 confirm 字段;表单组件注册三个输入并把各自错误以 alert 角色渲染,可访问性随声明获得。handleSubmit 只在整体通过校验后调用 onDone。
使用建议
异步校验(如邮箱占用)用 Hook Form 的异步校验钩子而非在 schema 里发请求。默认值显式声明避免受控切换警告。长表单拆字段组分级校验。
代码示例
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
const SignupSchema = z.object({
email: z.string().email('enter a valid email'),
password: z
.string()
.min(8, 'at least 8 characters')
.regex(/[A-Z]/, 'needs an uppercase letter')
.regex(/\d/, 'needs a digit'),
confirm: z.string(),
}).refine((data) => data.password === data.confirm, {
message: 'passwords do not match',
path: ['confirm'],
});
type SignupValues = z.infer<typeof SignupSchema>;
function SignupForm({ onDone }: { onDone: (values: SignupValues) => void }) {
const {
register,
handleSubmit,
formState: { errors, isSubmitting },
} = useForm<SignupValues>({
resolver: zodResolver(SignupSchema),
defaultValues: { email: '', password: '', confirm: '' },
});
return (
<form onSubmit={handleSubmit(onDone)}>
<input {...register('email')} placeholder="email" />
{errors.email && <p role="alert">{errors.email.message}</p>}
<input {...register('password')} type="password" placeholder="password" />
{errors.password && <p role="alert">{errors.password.message}</p>}
<input {...register('confirm')} type="password" placeholder="confirm" />
{errors.confirm && <p role="alert">{errors.confirm.message}</p>}
<button type="submit" disabled={isSubmitting}>Sign up</button>
</form>
);
}