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
相关产品推荐
相关产品推荐

