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

在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 06:46:00