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

React Hook Form中map内用useFormContext如何实现setValue/getValue

React Hook Form 遍历渲染TextField的设值、取值正确实现

原有代码核心问题

  • 所有遍历项的name属性都设为price,会导致表单字段互相覆盖,无法区分不同输入项
  • 子组件MyTextField未接收外部传入的name等属性,也没有把Controller提供的field配置绑定到TextField组件上,输入同步、值更新逻辑完全缺失
  • 未做表单上下文初始化、默认值配置,useFormContext无法正常生效

具体实现方案

1. 父组件初始化表单上下文

使用useForm初始化表单配置,设置数组字段默认值,用FormProvider包裹子组件树透传表单实例:

import { useForm, FormProvider } from 'react-hook-form'
import MyTextField from './MyTextField'
import { Button } from '@mui/material'

const ParentComponent = () => {
  // 初始化表单,配置默认值,prices对应遍历的输入项数组
  const methods = useForm({
    defaultValues: {
      prices: [100, 200, 300] // 对应初始渲染的数据源
    }
  })

  const { handleSubmit, setValue, watch, getValues } = methods

  // 表单提交时可直接拿到所有字段最新值
  const onSubmit = (formData) => {
    console.log('所有价格输入框最新值:', formData.prices) // 输出格式:[输入框1值, 输入框2值, 输入框3值]
  }

  // 手动设值示例:修改索引为1的输入框(第二个)值为500
  const updateSecondInput = () => {
    setValue('prices.1', 500, { shouldValidate: true })
  }

  // 实时取值示例:监听prices字段,值变化时自动同步
  const allPrices = watch('prices')
  // 监听单个字段写法:const secondPrice = watch('prices.1')

  return (
    <FormProvider {...methods}>
      <form onSubmit={handleSubmit(onSubmit)}>
        {/* 遍历渲染输入框,name必须带唯一索引区分字段 */}
        {getValues('prices').map((_, index) => (
          <MyTextField
            key={index}
            name={`prices.${index}`} // 唯一字段名,用索引区分不同输入项
            fullWidth
            sx={{ mb: 2 }}
          />
        ))}

        <Button onClick={updateSecondInput} variant="outlined" sx={{ mr: 2 }}>
          修改第二个输入框值为500
        </Button>
        <Button type="submit" variant="contained">提交表单</Button>

        <p>当前实时输入值:{JSON.stringify(allPrices)}</p>
      </form>
    </FormProvider>
  )
}

export default ParentComponent

2. 修复子组件MyTextField

接收name等外部传入属性,将Controller提供的field配置全量绑定到TextField,同时关联错误状态:

import { useFormContext, Controller } from 'react-hook-form'
import { TextField } from '@mui/material'

const MyTextField = ({ name, ...rest }) => {
  const { control } = useFormContext()

  return (
    <Controller
      control={control}
      name={name}
      render={({
        field: { onChange, onBlur, value, ref },
        fieldState: { error }
      }) => (
        <TextField
          onChange={onChange} // 绑定输入事件,自动同步值到表单状态
          onBlur={onBlur} // 绑定失焦事件,触发校验规则
          value={value || ''} // 兜底空值,避免undefined导致非受控组件警告
          inputRef={ref} // 绑定ref,用于聚焦、错误定位
          error={!!error} // 绑定错误状态
          helperText={error?.message} // 展示错误提示文案
          {...rest} // 透传fullWidth、样式等外部传入属性
        />
      )}
    />
  )
}

export default MyTextField

扩展:动态增减表单项场景

如果需要支持新增、删除输入项,直接使用useFieldArrayAPI即可,不需要手动维护数组结构:

import { useForm, FormProvider, useFieldArray } from 'react-hook-form'

const ParentComponent = () => {
  const methods = useForm({ defaultValues: { prices: [100,200,300] } })
  const { fields, append, remove } = useFieldArray({
    control: methods.control,
    name: 'prices'
  })

  return (
    <FormProvider {...methods}>
      <form>
        {fields.map((field, index) => (
          <div key={field.id} style={{marginBottom: 12}}>
            <MyTextField
              name={`prices.${index}`}
              fullWidth
            />
            <Button onClick={() => remove(index)} size="small">删除该项</Button>
          </div>
        ))}
        <Button onClick={() => append(0)} variant="contained">新增价格输入框</Button>
      </form>
    </FormProvider>
  )
}

关键注意事项

  • 遍历渲染的表单项name必须保证唯一,固定长度列表用字段名.索引格式即可,动态列表优先用useFieldArray
  • 不需要手动给MyTextField传value属性,所有值的同步、更新都交给React Hook Form管理,避免和组件内部状态冲突
  • 手动设值统一用setValue方法,需要实时响应值变化用watch,一次性取值用getValues,表单提交时直接从handleSubmit回调参数拿全量最新值即可

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 08:54:22