You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

能否在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 05:40:13