如何单独验证Zod Schema中的单个字段(NextJS场景)
解决方案
核心思路
不用给每个字段单独创建Schema,复用现有的createSchema就能实现字段级的onBlur验证,同时优化错误状态的更新逻辑,让修改后的合法字段自动清除错误提示。
步骤1:优化错误状态管理
确保错误状态是可单独更新的对象,用useState初始化:
import { useState } from 'react'; const [clientErrorMessage, setClientErrorMessage] = useState<Record<string, string[] | undefined>>({});
步骤2:创建单个字段验证函数
从主Schema中提取对应字段的验证规则,避免重复定义:
import { z } from 'zod'; // 假设你的主Schema是这样的(已有可跳过) const createSchema = z.object({ client_id: z.string().nonempty('请选择客户'), hours: z.number().min(10, 'Hours should be between 10 and 100').max(100, 'Hours should be between 10 and 100'), hours_left: z.number(), price: z.number().positive('价格必须为正数'), ends_at: z.string().nonempty('请选择截止日期'), }); // 单个字段验证函数 const validateSingleField = (fieldName: keyof typeof createSchema.shape, value: unknown) => { const fieldSchema = createSchema.shape[fieldName]; const result = fieldSchema.safeParse(value); return result.success ? null : result.error.issues[0].message; };
步骤3:给输入字段添加onBlur事件
在hours和price的输入框上绑定onBlur,触发单独验证并更新错误状态:
修改Hours字段:
<div className='mb-4'> <Label htmlFor='hours' className='mb-2'> Hours </Label> <Input type='number' name='hours' id='hours' required onBlur={(e) => { const value = Number(e.target.value); const error = validateSingleField('hours', value); // 更新对应字段的错误,保留其他字段错误 setClientErrorMessage(prev => ({ ...prev, hours: error ? [error] : undefined })); }} /> {clientErrorMessage?.hours && ( <p className='py-2 text-xs text-red-500'> {clientErrorMessage.hours} </p> )} </div>
修改Price字段:
<div className='mb-4'> <Label htmlFor='price' className='mb-2'> Price </Label> <div className='relative flex items-center max-w-2xl '> <Euro className='absolute left-2 top-1/2 h-4 w-4 -translate-y-1/2 transform' /> <Input type='number' name='price' id='price' required className='pl-6' onBlur={(e) => { const value = Number(e.target.value); const error = validateSingleField('price', value); setClientErrorMessage(prev => ({ ...prev, price: error ? [error] : undefined })); }} /> {clientErrorMessage?.price && ( <p className='py-2 text-xs text-red-500'> {clientErrorMessage.price} </p> )} </div> </div>
步骤4:修正表单提交时的验证函数
原来的validateFields存在返回值错误,修正后确保返回合法数据或null:
const validateFields = (formData: FormData) => { const formValues = { client_id: formData.get('client_id'), hours: Number(formData.get('hours')), hours_left: Number(formData.get('hours')), price: Number(formData.get('price')), ends_at: formData.get('ends_at'), }; const validationResult = createSchema.safeParse(formValues); if (!validationResult.success) { setClientErrorMessage(validationResult.error.flatten().fieldErrors); return null; // 验证失败返回null,终止后续操作 } return validationResult.data; // 返回验证通过的合法数据 };
步骤5:更新表单Action逻辑
确保只有验证通过时才调用服务端Action:
<form ref={formRef} action={async (formData) => { const validatedData = validateFields(formData); if (!validatedData) return; // 验证失败直接返回 const state = await createCard(validatedData); if (state.status === 'success') { formRef.current?.reset(); setOpen(false); setClientErrorMessage({}); // 成功后清空所有错误 setserverErrorMessage(undefined); } else if (state.status === 'error') { console.log(state); setserverErrorMessage(state.message); } }} > {/* 其他表单内容不变 */} </form>
关于是否需要单独创建Schema?
完全不需要。通过createSchema.shape[fieldName]可以直接获取对应字段的验证规则,既保证了字段验证规则和提交时的一致性,又避免了重复代码。
内容的提问来源于stack exchange,提问作者Santi
相关产品推荐
相关产品推荐

