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

react-hook-form+react-select+yup 条件下拉与默认值异常问题

问题原因

问题出在react-hook-form的Controller使用逻辑、react-select分组选项的取值逻辑上,和两个库本身的兼容性无关,核心错误有3个:

  • 分组选项结构为{label: 分组名, options: 子选项数组},原有代码用options.find(c => c.value === field.value)仅查找第一层数组,分组模式下第一层是分组对象,不存在value字段,永远返回undefined,导致UI无法匹配选中值
  • 传给Controller的defaultValue传入了完整option对象,但表单中该字段存储的是value字符串(onChange逻辑里只传了selectedOption.value),类型不匹配导致初始值映射失败
  • 条件渲染两种不同选项结构的SelectInput时未加唯一key,React会复用组件实例,切换模式时内部状态未重置
修复步骤

1. 改造SelectInput组件,兼容普通/分组选项

新增递归查找选项的方法,修正value映射逻辑,同时明确defaultValue接收字符串类型的值:

import * as React from "react";
import { Controller } from "react-hook-form";
import Select from "react-select";

// 递归查找选项,兼容平铺、分组两种结构
const findMatchedOption = (optionList, targetValue) => {
  for (const item of optionList) {
    if (item.options) {
      const childMatch = findMatchedOption(item.options, targetValue);
      if (childMatch) return childMatch;
    }
    if (item.value === targetValue) return item;
  }
  return null;
};

type SelectInputProps = {
  control: any;
  options: any;
  name: string;
  errors: any;
  handleChange?: (value: any) => void;
  defaultValue: string | number;
} & React.ComponentPropsWithoutRef<"div">;

export default function SelectInput({
  control,
  options,
  name,
  errors,
  handleChange,
  defaultValue
}: SelectInputProps) {
  return (
    <div>
      <Controller
        control={control}
        defaultValue={defaultValue}
        name={name}
        render={({ field }) => (
          <Select
            {...field}
            id={name}
            instanceId={name}
            options={options}
            value={findMatchedOption(options, field.value)}
            onChange={(selectedOption) => {
              field.onChange(selectedOption.value);
              handleChange?.(selectedOption.value);
            }}
          />
        )}
      />
      <p>{errors[name]?.message}</p>
    </div>
  );
}

额外提示:原组件中定义的register参数没有实际作用,Controller已经自动完成字段注册,可直接删除该参数定义和对应传参。

2. 修正App.js中的组件传参与渲染逻辑

给条件渲染的SelectInput加唯一key,强制切换类型时重挂载组件,同时将传入的defaultValue改为字符串值:

// 省略已有的options、schema定义代码
export default function App() {
  const {
    handleSubmit,
    control,
    watch,
    setValue,
    formState: { errors }
  } = useForm({
    defaultValues: schema.cast(),
    resolver: yupResolver(schema)
  });

  const watchMachineType = watch("machineType");

  return (
    <form onSubmit={handleSubmit((data) => console.log(data))}>
      <label>Machine Type</label>
      <SelectInput
        name="machineType"
        errors={errors}
        control={control}
        options={machineTypeOptions}
        defaultValue={schema.cast().machineType}
        handleChange={(value) => {
          if (value === "single") {
            setValue("machine", machineOptions[0].value, { shouldValidate: true });
          } else if (value === "multiple") {
            setValue("machine", machineGroupOptions[0].options[0].value, { shouldValidate: true });
          }
        }}
      />

      <label>Machine</label>
      {watchMachineType === "single" ? (
        <SelectInput
          key="single-machine-select"
          name="machine"
          errors={errors}
          control={control}
          defaultValue={machineOptions[0].value}
          options={machineOptions}
        />
      ) : (
        <SelectInput
          key="group-machine-select"
          name="machine"
          errors={errors}
          control={control}
          defaultValue={machineGroupOptions[0].options[0].value}
          options={machineGroupOptions}
        />
      )}

      <button type="submit">Submit</button>
    </form>
  );
}
修复效果
  • 页面初始加载时,分组模式下的默认值可正常显示在UI上
  • 切换machineType时,machine下拉框的选项结构、选中值和表单内部状态完全同步
  • 表单提交可拿到正确的字符串类型值,校验逻辑正常触发

注意:react-hook-form的Controller中,defaultValue和field.value存的是表单的原始业务值(也就是你最终提交需要的值,这里是字符串),react-select组件需要的option对象映射,要在render函数的value属性中单独处理,不要把整个option对象存在表单状态里。

内容的提问来源于stack exchange,提问作者Daniel Mason

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 05:06:27