React-Hook-Form中数组变更时如何重新渲染表单?
React Hook Form 动态添加配料表单不重渲染的修复方案
问题根源
你直接通过getValues().ingredients.push()修改数组的方式不符合React Hook Form的状态管理规则:
- React Hook Form的内部状态是不可变的,直接修改原数组不会触发表单状态更新,自然不会引发组件重渲染
- 当前代码中的输入框未正确注册到表单,即使渲染出来也无法收集数据
修复方案
- 使用
setValue更新数组:这是React Hook Form官方推荐的修改表单状态的方法,会触发状态更新和组件重渲染 - 正确注册动态字段:通过数组索引构建字段路径(如
ingredients[${index}].name),让React Hook Form能识别并收集每个配料的输入值 - 替换不稳定的key:不要用
Math.random()作为列表项的key,改用数组索引(动态列表无唯一ID时的合理选择) - 可选:监听数组变化:用
watch方法监听ingredients数组,确保组件能实时响应状态变化
修改后的完整代码
import FileInput from "../components/FileInput"; import Input from "../components/Input"; import Label from "../components/Label"; import TextArea from "../components/TextArea"; import { useForm, SubmitHandler, watch } from "react-hook-form"; interface Cocktail { name: string; description: string; imageUrl: string; ingredients: Ingredient[]; } interface Ingredient { name: string; quantity: string; unit: string; } const CreateCocktail = () => { const { register, setValue, handleSubmit } = useForm<Cocktail>({ defaultValues: { name: '', description: '', imageUrl: '', ingredients: [ {name: '', quantity: '', unit: ''} // 初始仅一行,符合需求 ] } }); // 监听ingredients数组变化,确保组件能响应状态更新 const ingredients = watch('ingredients'); const onAddIngredient = () => { // 用setValue创建新数组,触发状态更新 setValue('ingredients', [...ingredients, {name: '', quantity: '', unit: ''}]); } const onSubmit: SubmitHandler<Cocktail> = async data => { try { console.log("提交的鸡尾酒数据", data); data.description = "测试描述"; data.imageUrl = "测试图片链接"; const response = await fetch('/api/cocktails', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(data), }); if (response.ok) { console.log('鸡尾酒创建成功'); } else { console.log('创建鸡尾酒失败'); } } catch (error) { console.log(error); } }; return ( <> <div> <form onSubmit={handleSubmit(onSubmit)}> {/* 其他表单部分省略 */} <Label htmlFor="ingredients" text="配料" /> <div> {ingredients.map((_, index) => ( <div key={index}> <input type="text" placeholder="淡朗姆酒" {...register(`ingredients[${index}].name`)} /> <input type="text" placeholder="60" {...register(`ingredients[${index}].quantity`)} /> <select {...register(`ingredients[${index}].unit`)}> <option value="ml">ml</option> <option value="dashes">滴</option> <option value="stk">个</option> </select> </div> ))} <div> <button type="button" onClick={onAddIngredient}> 添加配料 </button> </div> </div> <div> <input type="submit" value="保存" /> </div> </form> </div> </> ); } export default CreateCocktail;
关键修改说明
- 用
setValue('ingredients', [...ingredients, newItem])创建新数组,替代直接push修改原数组,确保触发React Hook Form的状态更新 - 用
watch('ingredients')获取实时的配料数组,作为渲染列表的数据源 - 每个输入框通过
register(ingredients[${index}].xxx)注册到表单,确保数据能被正确收集 - 用数组索引
index作为列表项的key,避免Math.random()带来的不稳定渲染问题
内容的提问来源于stack exchange,提问作者Jakob Busk Sørensen
相关产品推荐
相关产品推荐

