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

React-Hook-Form无法读取状态对象字段问题求助

问题描述

我使用react-select异步组件向API发起请求,希望将返回对象的各字段显示在React-Hook-Form(RHF)的输入框中。目前遇到的问题是:这些字段注册后能成功写入表单状态,但完全不响应状态变化——无论是通过异步组件选择选项,还是显式调用setValue设置表单状态,输入框都不会显示对应的值。

我尝试了两种绑定方式:直接用register绑定input,以及用Controller包裹input,但两种方式都无法让输入框响应表单状态更新。代码如下:

const formMethods = useForm<FormFields>({
    defaultValues: {
      todo: {
        title: "",
        id: "",
      },
    },
  });

  return (
      <div className="row">
        <Controller
          control={formMethods.control}
          name="todo"
          render={(params) => (
            <AsyncCreatableSelect
              defaultOptions
              getOptionLabel={(todo) => todo.title}
              getOptionValue={(todo) => todo.id}
              loadOptions={promiseOptions}
              {...params.field}
            />
          )}
        />
        <div>
          <Controller
            control={formMethods.control}
            name="todo.id"
            render={(params) => {
              return (
                <label>
                  Controller-bound
                  <input {...params.field} value={params.field.value} />
                </label>
              );
            }}
          />
        </div>
        <label>
          Register-bound
          <input {...formMethods.register("todo.title")} />
        </label>
        <div>
          <button
            onClick={() => {
              formMethods.setValue(
                "todo",
                {
                  userId: 1,
                  id: 1,
                  title: "delectus aut autem",
                  completed: false,
                },
                { shouldDirty: true, shouldTouch: true }
              );
            }}
          >
            Set Object Value
          </button>
        </div>
      </div>
解决方案

1. 统一类型与默认值结构

首先确保FormFields的类型定义和API返回的Todo结构完全匹配,同时让默认值的结构和类型与更新对象一致,避免类型不兼容导致的状态同步异常:

// 定义完整的Todo类型
type Todo = {
  userId: number;
  id: number;
  title: string;
  completed: boolean;
};

type FormFields = {
  todo: Todo;
};

// 初始化表单时使用匹配的默认值
const formMethods = useForm<FormFields>({
  defaultValues: {
    todo: {
      userId: 0,
      id: 0,
      title: "",
      completed: false,
    },
  },
});

2. 修正react-select的getOptionValue

react-select要求getOptionValue返回字符串类型,若API返回的id是数字,需转为字符串:

<AsyncCreatableSelect
  defaultOptions
  getOptionLabel={(todo) => todo.title}
  // 将数字id转为字符串
  getOptionValue={(todo) => todo.id.toString()}
  loadOptions={promiseOptions}
  {...params.field}
/>

3. 优化输入框的绑定逻辑

  • 对于Controller绑定的输入框,无需手动指定value={params.field.value},直接展开{...params.field}即可,Controller已自动处理值的同步:
<Controller
  control={formMethods.control}
  name="todo.id"
  render={(params) => (
    <label>
      Controller-bound
      <input {...params.field} />
    </label>
  )}
/>
  • 直接使用register绑定的输入框无需额外调整,确保字段路径todo.title与表单状态结构匹配即可。

修正后的完整关键代码

type Todo = {
  userId: number;
  id: number;
  title: string;
  completed: boolean;
};

type FormFields = {
  todo: Todo;
};

export default function App() {
  const formMethods = useForm<FormFields>({
    defaultValues: {
      todo: {
        userId: 0,
        id: 0,
        title: "",
        completed: false,
      },
    },
  });

  const promiseOptions = async (inputValue: string) => {
    const res = await fetch(`https://jsonplaceholder.typicode.com/todos?q=${inputValue}`);
    const todos = await res.json();
    return { options: todos };
  };

  return (
    <div className="row">
      <Controller
        control={formMethods.control}
        name="todo"
        render={(params) => (
          <AsyncCreatableSelect
            defaultOptions
            getOptionLabel={(todo) => todo.title}
            getOptionValue={(todo) => todo.id.toString()}
            loadOptions={promiseOptions}
            {...params.field}
          />
        )}
      />
      <div>
        <Controller
          control={formMethods.control}
          name="todo.id"
          render={(params) => (
            <label>
              Controller-bound
              <input {...params.field} />
            </label>
          )}
        />
      </div>
      <label>
        Register-bound
        <input {...formMethods.register("todo.title")} />
      </label>
      <div>
        <button
          onClick={() => {
            formMethods.setValue(
              "todo",
              {
                userId: 1,
                id: 1,
                title: "delectus aut autem",
                completed: false,
              },
              { shouldDirty: true, shouldTouch: true }
            );
          }}
        >
          Set Object Value
        </button>
      </div>
    </div>
  );
}

问题根源总结

  1. 类型不匹配:初始默认值的todo.id为字符串,而setValue传入的是数字,导致RHF无法正确同步值到输入框;
  2. react-select配置错误:getOptionValue返回数字而非字符串,导致选中选项后存储的todo.id类型不一致;
  3. 默认值结构缺失字段:初始默认值缺少userId和completed字段,更新完整对象时RHF状态同步异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 14:04:53