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

react-hook-form的watch方法在select元素渲染后获取初始值异常问题

问题根因

这个问题的本质是首次渲染时select组件注册到react-hook-form时,下拉选项还没从服务端拉取回来,此时select没有可选中的<option>,react-hook-form会将该字段的初始值设为空字符串。后续下拉选项填充完成后,浏览器会默认选中第一个选项,但这个DOM层面的默认选中行为没有触发表单的change事件,因此react-hook-form无法感知到值的变化,watch返回的值还是之前的空字符串。

可行解决方案

方案1:下拉选项加载完成后手动赋值

使用react-hook-form提供的setValue方法,在下拉选项请求完成后主动给字段赋初始值:

import React, { useState, useEffect } from 'react';
import { useForm } from "react-hook-form";

function App() {
  const { register, watch, setValue } = useForm();
  const watchFoo = watch("foo");
  const [foos, setFoos] = useState([]);

  useEffect(() => {
    const newFoos = [{ id: 1, name: "one" }, { id: 2, name: "two" }];
    setFoos(newFoos);
    // 选项加载完成后手动设置初始值
    setValue('foo', newFoos[0].id);
  }, []);

  return (
    <form>
      <select {...register('foo')}>
        {foos.map((foo, i) => (<option key={`option_${i}`} value={foo.id}>{foo.name}</option>))}
      </select>
      <p>foo is {watchFoo}</p>
    </form>
  );
}

export default App;

这个方案是最常用的,逻辑简单清晰,不需要修改组件渲染逻辑。

方案2:延迟渲染select组件

等下拉选项加载完成后再渲染select,保证注册到react-hook-form时已经有可用的选项:

import React, { useState, useEffect } from 'react';
import { useForm } from "react-hook-form";

function App() {
  const { register, watch } = useForm();
  const watchFoo = watch("foo");
  const [foos, setFoos] = useState([]);

  useEffect(() => {
    setFoos([{ id: 1, name: "one" }, { id: 2, name: "two" }]);
  }, []);

  return (
    <form>
      {/* 选项加载完成后才渲染select */}
      {foos.length > 0 && (
        <select {...register('foo')}>
          {foos.map((foo, i) => (<option key={`option_${i}`} value={foo.id}>{foo.name}</option>))}
        </select>
      )}
      <p>foo is {watchFoo}</p>
    </form>
  );
}

export default App;

如果可以接受加载选项期间不展示下拉框,这个方案也非常简洁。

方案3:使用Controller实现受控组件

如果是复杂的表单场景,也可以用react-hook-form提供的Controller组件将下拉框改成受控组件,直接控制值的变化:

import React, { useState, useEffect } from 'react';
import { useForm, Controller } from "react-hook-form";

function App() {
  const { control, watch } = useForm();
  const watchFoo = watch("foo");
  const [foos, setFoos] = useState([]);

  useEffect(() => {
    setFoos([{ id: 1, name: "one" }, { id: 2, name: "two" }]);
  }, []);

  return (
    <form>
      <Controller
        name="foo"
        control={control}
        defaultValue={foos[0]?.id}
        render={({ field }) => (
          <select {...field}>
            {foos.map((foo, i) => (<option key={`option_${i}`} value={foo.id}>{foo.name}</option>))}
          </select>
        )}
      />
      <p>foo is {watchFoo}</p>
    </form>
  );
}

export default App;

这个方案适合和其他受控组件配合使用的场景,值的更新逻辑完全由react-hook-form管控,不会出现同步问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 10:36:03