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

React Hook Form中Controller返回[Function Anonymous]而非数组数据问题

解决SkillsInput组件返回函数而非数组导致的Yup校验错误

问题根源

核心问题是SkillsInput组件内部嵌套了独立的useForm实例,和父组件Detail的form状态完全隔离:

  • 子组件内部的items是自身form实例的状态,虽然打印正常,但父组件根本拿不到这个值
  • 父组件通过自身Controller获取子组件的值时,实际拿到的是子组件内部某个函数引用(即你看到的[Function Anonymous]),最终被转换为null触发Yup的数组类型校验错误

修复方案

不要在子组件里单独用useForm,而是复用父组件的form控制实例(control)和字段名(name),让子组件的状态直接挂载到父组件的form状态中。

1. 改造SkillsInput组件

移除内部的useForm,改为接收父组件传递的control和name,用useWatch监听字段值,直接通过父组件的control.setValue操作数组:

import { Controller, useWatch } from "react-hook-form";

const SkillsInput = ({ control, name }) => {
  // 监听父组件form中对应字段的值
  const items = useWatch({ control, name });

  // 添加技能:更新父组件form的skills数组
  const addSkill = () => {
    control.setValue(name, [...items, ""]);
  };

  // 删除指定索引的技能
  const removeSkill = (index) => {
    const updatedItems = items.filter((_, i) => i !== index);
    control.setValue(name, updatedItems);
  };

  // 更新指定索引的技能值
  const updateSkill = (index, value) => {
    const updatedItems = [...items];
    updatedItems[index] = value;
    control.setValue(name, updatedItems);
  };

  return (
    <Controller
      control={control}
      name={name}
      render={({ field }) => (
        <div>
          {field.value.map((skill, index) => (
            <div key={index} style={{ margin: "8px 0" }}>
              <input
                value={skill}
                onChange={(e) => updateSkill(index, e.target.value)}
                placeholder="输入技能"
              />
              <button onClick={() => removeSkill(index)} style={{ marginLeft: "8px" }}>
                删除
              </button>
            </div>
          ))}
          <button onClick={addSkill}>添加技能</button>
        </div>
      )}
    />
  );
};

export default SkillsInput;

2. 在Detail组件中使用改造后的SkillsInput

传递父组件的control和字段名skills,同时保持Yup校验规则不变:

import { useForm, Controller } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
import * as Yup from "yup";
import SkillsInput from "./SkillsInput";

// Yup校验规则
const formSchema = Yup.object().shape({
  skills: Yup.array()
    .of(Yup.string().required("技能不能为空"))
    .required("至少添加一项技能"),
  // 其他字段规则...
});

const Detail = () => {
  const { control, handleSubmit, formState: { errors } } = useForm({
    defaultValues: {
      skills: [],
      // 其他字段默认值...
    },
    resolver: yupResolver(formSchema),
  });

  const onSubmit = (data) => {
    console.log("提交数据:", data);
    // 这里的data.skills就是正确的数组类型
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)} style={{ padding: "20px" }}>
      <h3>技能输入</h3>
      <SkillsInput control={control} name="skills" />
      {errors.skills && <p style={{ color: "red" }}>{errors.skills.message}</p>}

      {/* PreferencesInput组件同理,传递control和对应name */}
      {/* <PreferencesInput control={control} name="preferences" /> */}

      <button type="submit" style={{ marginTop: "20px" }}>提交</button>
    </form>
  );
};

export default Detail;

3. 校验规则说明

如果需要允许skills为空数组,可以把Yup规则改成:

skills: Yup.array().of(Yup.string().required()).nullable()

但根据你的需求,保持原规则即可,只要父组件能正确拿到数组值,校验就会正常通过。

补充说明

react-hook-form的每个useForm实例都是独立的状态容器,嵌套使用会导致状态割裂,父组件无法感知子组件的内部状态变化。复用父组件的control是实现嵌套表单组件的正确方式,PreferencesInput组件也需要按照同样的逻辑改造。

内容的提问来源于stack exchange,提问作者etcN00b

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 10:08:14