能否在Zod的superRefine或其调用的Hook中设置React状态?
在Zod superRefine中设置React状态的问题解决方案
核心结论
不能直接在Zod的superRefine函数或其内部调用的Hook中设置React状态,你遇到的错误是正常现象——因为Zod校验逻辑(包括superRefine)属于纯校验流程,而React状态更新依赖组件渲染上下文,脱离上下文调用状态Hook会导致内部对象未定义,触发类似Cannot read properties of undefined (reading 'length')的错误。
可行实现方案
1. 分离校验与状态更新逻辑
将数据请求、状态更新逻辑从Zod校验中抽离,放在表单提交、输入事件或组件副作用里,确保状态更新在组件上下文内执行:
import { useForm } from "react-hook-form"; import { z } from "zod"; import { zodResolver } from "@hookform/resolvers/zod"; import axios from "axios"; import { useState } from "react"; // 纯校验逻辑的Zod Schema const formSchema = z.object({ keyword: z.string().min(2, "关键词至少2个字符") }); export default function SearchForm() { const [searchResult, setSearchResult] = useState(null); const { register, handleSubmit, setError } = useForm({ resolver: zodResolver(formSchema) }); // 提交时先过校验,再执行请求和状态更新 const handleSearch = async (formData) => { try { const res = await axios.get("/api/search", { params: formData }); setSearchResult(res.data); } catch (err) { // 请求失败时手动设置表单错误 setError("keyword", { message: "搜索失败,请重试" }); } }; return ( <form onSubmit={handleSubmit(handleSearch)}> <input {...register("keyword")} placeholder="输入关键词" /> <button type="submit">搜索</button> {searchResult && <div>搜索结果:{JSON.stringify(searchResult)}</div>} </form> ); }
2. 异步校验后通过副作用更新状态
如果需要在校验过程中做异步操作(比如校验值是否合法),可在superRefine中完成异步校验并设置表单错误,再通过useEffect监听表单状态变化来更新组件状态:
import { useForm } from "react-hook-form"; import { z } from "zod"; import { zodResolver } from "@hookform/resolvers/zod"; import axios from "axios"; import { useState, useEffect } from "react"; const formSchema = z.object({ email: z.string().email("请输入合法邮箱").superRefine(async (email, ctx) => { // 异步校验邮箱是否已注册 const isRegistered = await axios.get(`/api/check-email?email=${email}`); if (isRegistered.data) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: "该邮箱已注册" }); } }) }); export default function RegisterForm() { const [emailStatus, setEmailStatus] = useState(""); const { register, formState: { errors } } = useForm({ resolver: zodResolver(formSchema) }); // 监听表单错误,更新组件状态 useEffect(() => { if (errors.email) { setEmailStatus(errors.email.message); } else if (register("email").value) { setEmailStatus("邮箱可用"); } else { setEmailStatus(""); } }, [errors.email, register("email").value]); return ( <div> <input {...register("email")} placeholder="输入邮箱" /> <p>{emailStatus}</p> </div> ); }
错误原因说明
你遇到的Cannot read properties of undefined (reading 'length')错误,本质是因为superRefine的异步校验函数执行时,可能脱离了React组件的渲染上下文(比如校验触发时组件已卸载,或者Hook调用未在组件渲染周期内),导致React内部管理状态更新的相关对象为undefined,进而触发读取length的异常。
内容的提问来源于stack exchange,提问作者Federico Ferrari
相关产品推荐
相关产品推荐

