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

React-Hook-Form中数组变更时如何重新渲染表单?

React Hook Form 动态添加配料表单不重渲染的修复方案

问题根源

你直接通过getValues().ingredients.push()修改数组的方式不符合React Hook Form的状态管理规则:

  • React Hook Form的内部状态是不可变的,直接修改原数组不会触发表单状态更新,自然不会引发组件重渲染
  • 当前代码中的输入框未正确注册到表单,即使渲染出来也无法收集数据

修复方案

  1. 使用setValue更新数组:这是React Hook Form官方推荐的修改表单状态的方法,会触发状态更新和组件重渲染
  2. 正确注册动态字段:通过数组索引构建字段路径(如ingredients[${index}].name),让React Hook Form能识别并收集每个配料的输入值
  3. 替换不稳定的key:不要用Math.random()作为列表项的key,改用数组索引(动态列表无唯一ID时的合理选择)
  4. 可选:监听数组变化:用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 21:47:20