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

NextUI多选组件结合React Hook Form的TypeScript错误问题

解决NextUI多选组件结合React Hook Form的TypeScript类型不兼容问题

错误核心原因

错误源于React Hook Form的Controller绑定的groups字段类型是ProfileGroup[],但NextUI Select组件的value属性要求与选项匹配的string[]类型。代码中通过{...field}把Controller的field.value(ProfileGroup[])传递给了Select,直接触发类型不兼容报错。

解决方案

方案1:拆分Controller字段传递,避免value冲突

不需要把field.value传给Select,用selectedKeys控制选中状态,在onGroupChanged里手动更新表单的groups值,仅传递field.onBlur触发表单校验:

const ProfileDetails: React.FC<ProfileDetailProps> = ({ profileId }) => {
  const {
    register,
    handleSubmit,
    setValue,
    reset,
    control,
    formState: { errors }
  } = useForm<Profile>({
    defaultValues: profile
  })
  const [countries, setCountries] = useState<string[]>([])

  const onGroupChanged = (groups: string[]) => {
    const profileGroups: ProfileGroup[] = []
    groups.forEach((item: string) => {
      const [countryCode, groupName] = item.split(" ")
      profileGroups.push({
        profile_id: profile?.id,
        country_code: countryCode,
        group: groupName
      })
    })
    setValue("groups", profileGroups)
    setCountries(groups)
  }

  useEffect(() => {
    if (profile) {
      reset(profile)
      // 从profile的groups转换为Select需要的string[]格式
      const formattedKeys = profile.groups?.map(g => `${g.country_code} ${g.group}`) ?? []
      setCountries(formattedKeys)
    }
  }, [profile, reset])

  return (
    <div>
      <form onSubmit={handleSubmit(onSubmit)}>
        <Controller
          name="groups"
          control={control}
          rules={{ required: "Country group is required" }}
          render={({ field: { onBlur } }) => (
            <Select
              items={countryGroupOptions}
              label="Country"
              labelPlacement="outside"
              placeholder="Select"
              selectionMode="multiple"
              selectedKeys={countries}
              onChange={onGroupChanged}
              onBlur={onBlur}
            >
              {(item) => <SelectItem key={item.value}>{item.label}</SelectItem>}
            </Select>
          )}
        ></Controller>
      </form>
    </div>
  )
}

方案2:使用Controller类型转换能力(更内聚)

利用Controller的字段转换逻辑,让表单内部存储ProfileGroup[],自动转换为Select需要的string[]:

const ProfileDetails: React.FC<ProfileDetailProps> = ({ profileId }) => {
  const {
    register,
    handleSubmit,
    reset,
    control,
    formState: { errors }
  } = useForm<Profile>({
    defaultValues: profile
  })

  // 将选中的string[]转为ProfileGroup[]
  const toProfileGroups = (selectedKeys: string[]) => {
    return selectedKeys.map(item => {
      const [countryCode, groupName] = item.split(" ")
      return {
        profile_id: profile?.id,
        country_code: countryCode,
        group: groupName
      }
    })
  }

  // 将ProfileGroup[]转为Select需要的string[]
  const toSelectKeys = (groups: ProfileGroup[]) => {
    return groups.map(g => `${g.country_code} ${g.group}`)
  }

  return (
    <div>
      <form onSubmit={handleSubmit(onSubmit)}>
        <Controller
          name="groups"
          control={control}
          rules={{ required: "Country group is required" }}
          render={({ field: { value, onChange, onBlur } }) => (
            <Select
              items={countryGroupOptions}
              label="Country"
              labelPlacement="outside"
              placeholder="Select"
              selectionMode="multiple"
              selectedKeys={toSelectKeys(value)}
              onChange={(keys) => onChange(toProfileGroups(keys as string[]))}
              onBlur={onBlur}
            >
              {(item) => <SelectItem key={item.value}>{item.label}</SelectItem>}
            </Select>
          )}
        ></Controller>
      </form>
    </div>
  )
}

关键说明

  • 方案1通过本地状态维护Select选中状态,逻辑直观,适合简单场景。
  • 方案2利用React Hook Form的转换能力,避免额外状态,逻辑更内聚。
  • 两种方案均避免了将ProfileGroup[]直接传递给Select的value属性,彻底解决TypeScript类型冲突。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 12:59:52