ReactHookForm+ReactQuery+Zod隐藏默认值赋值及表单提交问题排查
问题解决:React Hook Form + React Query 表单关联用户失败&重复渲染问题
核心问题诊断
- 提交无响应:Zod校验失败导致表单无法提交。你在
fetchUser里用profileFormSchema.parse({ id: res.data.data }),但Schema定义的字段是user_id,且类型为string|null,但返回的res.data.data是number类型,解析失败后表单处于错误状态,直接阻止了提交动作。 - 重复打印id/重复渲染:React Query因
fetchUser中的Zod解析错误触发重试机制,导致接口反复调用,id重复打印;同时隐藏字段的错误写法引发组件不必要的重复渲染。 - 隐藏字段传递错误:手动给hidden input设置
value={data.user_id}会覆盖React Hook Form的字段控制逻辑,导致表单状态混乱,进一步影响提交逻辑。
修正步骤
1. 修复用户数据获取与解析
修改fetchUser函数,返回符合Schema结构的数据,并转换id类型:
async function fetchUser() { const res = await axios.get('/api/get-user'); const userId = res.data.data; console.log('id:', userId); // 转换为string类型,匹配user_id的schema定义 return { user_id: userId?.toString() || null }; }
2. 正确关联用户ID到表单
方式一:用React Hook Form的setValue设置(推荐)
不需要隐藏字段,在React Query获取到用户数据后,直接设置表单字段:
const { data: userData } = useQuery({ queryKey: ['current-user'], queryFn: fetchUser, retry: 1, // 限制重试次数,避免接口反复调用 }); // 用户数据加载完成后同步到表单 useEffect(() => { if (userData?.user_id) { form.setValue('user_id', userData.user_id); } }, [userData, form]);
方式二:修正隐藏字段写法(不推荐)
如果坚持使用隐藏字段,不要手动覆盖value,让React Hook Form完全控制字段:
// 初始化表单时传入user_id默认值 const form = useForm<ProfileFormValues>({ resolver: zodResolver(profileFormSchema), defaultValues: { ...defaultValues, user_id: userData?.user_id || null, }, mode: 'onChange', }); // 隐藏字段仅绑定field,无需手动设置value <FormField name='user_id' control={form.control} render={({ field }) => ( <input type='hidden' {...field} /> )} />
3. 更安全的提交逻辑(最优方案)
完全不在表单中维护user_id字段,提交时直接将用户ID加入请求体,避免前端暴露或篡改风险:
// 修改Zod Schema,移除user_id字段(不再作为表单输入项) const profileFormSchema = z.object({ email: z.string().min(2).max(320).email(), title: z.string({ required_error: 'Please provide a title for your e-mail.' }), content: z.string().max(10000).min(4), can_share: z.boolean(), }); type ProfileFormValues = z.infer<typeof profileFormSchema>; // 提交时合并用户ID async function onSubmit(formData: ProfileFormValues) { const submitData = { ...formData, user_id: userData?.user_id || null, }; const res = await fetch('/api/create', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(submitData), }); const json = await res.json(); if (json.status === 201) { toast.success('Template created!'); setUrl(json.data); } }
额外优化点
- 给React Query设置
retry:1,避免接口错误时反复调用。 - 实现未登录跳转逻辑:
const { data: userData, isError } = useQuery({ queryKey: ['current-user'], queryFn: fetchUser, retry: 1, }); if (isError || !userData?.user_id) { redirect('/login'); }
内容的提问来源于stack exchange,提问作者hey
相关产品推荐
相关产品推荐

