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

如何通过react-hook-form向子组件传递表单初始值?

问题分析与优化方案

你的代码确实能实现表单初始值填充的需求,但存在几个可以优化的点,比如不必要的无限循环渲染,以及子组件和表单逻辑的耦合问题。React Hook Form其实有更优雅的方式来处理自定义子组件的初始值和状态同步,下面给你拆解最优实践:


现有代码的问题

  • 无限循环渲染:两个组件里的useEffect都没有设置依赖数组,这会导致每次组件渲染时都会执行setValue,触发重新渲染,形成无限循环(你可以在控制台看看console.log(year)是不是一直在重复输出)。
  • 子组件耦合度高:把子组件的register和setValue直接从父组件传递,让YearSelect和表单逻辑强绑定,复用性变差。

最优解决方案:使用Controller组件

React Hook Form提供了Controller组件,专门用来处理自定义受控组件,它会帮你管理组件的注册、值同步和验证,不需要手动传递register或setValue,代码更简洁解耦。

修改后的完整代码:

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

function YearSelect({ value, onChange }) {
  return (
    <select value={value} onChange={(e) => onChange(e.target.value)}>
      <option value="">---</option>
      <option value="2021">2021</option>
      <option value="2022">2022</option>
    </select>
  )
}

function App() {
  // 1. 优先用defaultValues设置初始值,这是React Hook Form的推荐方式
  const { register, handleSubmit, control, setValue } = useForm({
    defaultValues: {
      title: '',
      year: ''
    }
  });

  // 2. 模拟API调用后填充初始值,添加空依赖数组确保只执行一次
  useEffect(() => {
    // 假设这里是API请求逻辑
    setTimeout(() => {
      setValue('title', 'HEY');
      setValue('year', '2021');
    }, 1000);
  }, [setValue]); // 依赖setValue,避免不必要的重复执行

  const onSubmit = data => {
    console.log(data);
    alert('表单提交成功');
  }

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      Title: <input name="title" {...register('title')}/><br/>
      Year: 
      {/* 用Controller包裹自定义组件,作为表单和组件的桥梁 */}
      <Controller
        name="year"
        control={control}
        render={({ field: { value, onChange } }) => (
          <YearSelect value={value} onChange={onChange} />
        )}
      />
      <button type="submit">Save</button>
    </form>
  );
}

export default App;

方案说明

  1. 用defaultValues初始化:这是React Hook Form官方推荐的初始值设置方式,表单加载时会自动填充对应字段的值,避免手动调用setValue的繁琐。
  2. Controller的作用:它作为表单和自定义组件之间的桥梁,自动处理值的同步、注册和验证逻辑,子组件只需要接收value和onChange,完全和表单逻辑解耦,复用性更强。
  3. 修复useEffect循环:给useEffect添加依赖数组[setValue],确保只有在setValue变化时才执行(实际上setValue是useForm返回的稳定函数,所以这个effect只会执行一次),避免无限循环。

另一种简化方案(不使用Controller)

如果你不想用Controller,也可以通过传递control给子组件,用control.register来注册字段,同时利用watch获取字段值,不过这种方式还是不如Controller优雅:

// YearSelect组件修改
function YearSelect({ control, watch }) {
  const yearValue = watch('year', ''); // 第二个参数是默认值
  return (
    <select 
      {...control.register('year')}
      value={yearValue}
    >
      <option value="">---</option>
      <option value="2021">2021</option>
      <option value="2022">2022</option>
    </select>
  )
}

// App组件中使用
<YearSelect control={control} watch={watch} />

但这种方式还是需要子组件依赖表单的control和watch,不如Controller的解耦效果好,所以优先推荐第一种方案。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 15:27:28