在React Hook Form的Field Array中使用React Select时Values对象不更新
问题描述
用react-hook-form的Field Array实现动态变体表单,搭配支持多选的react-select收集选项值时遇到两个问题:
- 更新values字段时,variations对象未同步,导致预览表格无法更新
- 新增选项后,option字段能正常显示,但为该选项添加值后,values对象始终为空
错误原因及修复方案
1. ReactSelect组件创建了独立表单实例
ReactSelect内部自行调用useForm(),生成了和主组件无关的新表单实例,导致选中的值无法同步到主表单的variations数组中。必须从主组件传递control和errors,而非在组件内部初始化表单。
2. 新增选项时初始字段不匹配
主组件append新选项时使用{ option: "", value: "" },但实际需要的字段是values(数组类型),导致新增的选项没有对应的values字段,无法存储选中值。要把append的初始值改为{ option: "", values: [] }。
3. 多选默认值处理错误
getDefaultValue函数把数组取第一个元素,不符合react-select的isMulti要求(需直接传入数组),导致初始值无法正确渲染,也影响后续值的同步。直接返回传入的value数组即可。
修改后的代码
主组件(VariationSetup.jsx)
import React from "react"; import { useForm, useFieldArray } from "react-hook-form"; import ReactSelect from "./Select"; import Input from "./Input"; import VariationPreview from "./VariationPreview"; const initialValues = { variations: [ { id: 1, option: "Size", values: [ { id: 1, label: "SM", value: "sm" }, { id: 2, label: "MD", value: "md" } ] }, { id: 2, option: "Color", values: [ { id: 1, label: "Red", value: "red" }, { id: 2, label: "Blue", value: "blue" } ] } ] }; const VariationSetup = () => { const { register, control, handleSubmit, formState: { errors }, watch, getValues } = useForm({ defaultValues: initialValues ?? {} }); const { fields, append, remove } = useFieldArray({ control, name: "variations" }); const variants = watch("variations"); console.log("variants", variants); return ( <> <p className="font-semibold mb-4 text-md">Options</p> {fields.map((field, index) => { return ( <React.Fragment key={field.id}> <div className="flex mb-4" key={field.id}> <div className="w-full md:w-4/12"> <Input name={`variations.${index}.option`} label="Option" placeholder="Choose option" options={VARIATION_OPTION} helperText="Choose option that can be applied as variants for a product" errors={errors} register={register} /> </div> <div className="w-full md:ml-4 md:w-8/12"> <ReactSelect name={`variations.${index}.values`} label="" placeholder="Choose value" options={[]} helperText="You can choose multiple values" wrapperClassName="mt-7" errors={errors} control={control} isCreateable /> </div> </div> </React.Fragment> ); })} <button className="bg-gray-200 p-3" type="button" onClick={() => append({ option: "", values: [] })} > Add option </button> <div className="divide-y"></div> <VariationPreview variations={watch("variations")} /> </> ); }; export default VariationSetup; const VARIATION_OPTION = [ { id: 1, label: "Size", value: "size" }, { id: 2, label: "Color", value: "color" } ];
ReactSelect组件(Select.jsx)
import { Controller } from "react-hook-form"; import Select from "react-select"; import CreatableSelect from "react-select/creatable"; // 自定义样式示例(可根据需求调整) const customStyles = {}; const errorStyles = {}; export default function ReactSelect({ disabled, label, helperText, name, placeholder, options, defaultValue, className, labelClassName, wrapperClassName, isCreateable = false, control, errors }) { const getDefaultValue = (value) => { return value || []; }; return ( <div className={wrapperClassName ? wrapperClassName : ""}> <label htmlFor={name} className={ labelClassName ? labelClassName : "block font-semibold mb-2 text-gray-700 text-sm tracking-wide uppercase" } > {label} </label> <div className="mt-1 relative"> <Controller name={name} defaultValue={getDefaultValue(defaultValue)} control={control} render={({ field }) => { const styles = errors?.[name] ? errorStyles : customStyles; if (isCreateable) { return ( <CreatableSelect isMulti {...field} isDisabled={disabled} placeholder={placeholder} options={options} styles={styles} className={className} /> ); } else { return ( <Select {...field} isDisabled={disabled} placeholder={placeholder} options={options} styles={styles} className={className} /> ); } }} /> </div> </div> ); }
内容的提问来源于stack exchange,提问作者Serenity
相关产品推荐
相关产品推荐

