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

RadixUI Select组件与react-hook-form集成失效问题求助

解决React Hook Form与Radix UI Select的取值问题

问题背景

尝试通过React Hook Form获取Radix UI Select组件的选中值,使用Controller组件或forwardRef都未达到预期效果,测试代码如下:

'use client';
import React from 'react';
import { useCurrentUser } from '@/hooks/useCurrentUser';
import { FormField } from '../ui/forms';
import { TextArea } from '../ui/forms/TextArea';
import DropdownCause from './DropdownCause';
import DropdownShop from './DropdownShops';
import Button from '../ui/Button';
import { DataPicker } from '../ui/forms/DataPicker';
import { useForm, Controller } from 'react-hook-form';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@radix-ui/react-select';
import DropdownNewTicket from './DropdownNewTicket';
import { MdArrowDropDown } from 'react-icons/md'
const fakeData = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]


export default function NewTicketForm() {

  const session = useCurrentUser();
  let name = session?.name ? session.name : ' ';

  const { handleSubmit, register, control } = useForm({
    mode: 'all',
    reValidateMode: 'onChange',
    //resolver: zodResolver()
  });

  const createTicket = (data: any) => {
    console.log(data);
  };

  return (
    <div className='border-2 border-primary p-3 rounded justify-center self-center w-2/3 h-5/6 scrollbar-thumb-slate-700 scrollbar-thin scrollbar-thumb-rounded flex flex-col items-center overflow-y-scroll'>
      <form className='flex flex-col gap-6' onSubmit={handleSubmit(createTicket)}>
        <FormField fieldName='Nome' className='w-80 rounded border border-primary indent-1 text-lg' value={name} {...register('username')} />
        <DataPicker fieldName='Data da venda' {...register('sale-date')} />
        <Controller
          control={control}
          name="product"
          rules={{ required: true }}
          render={({ field }) => (
            <Select onValueChange={field.onChange} {...field} defaultValue='312321'>
              <SelectTrigger className='w-80 h-fit rounded-t rounded-tl border border-primary text-lg flex' asChild>
                <div className='flex justify-center'>
                  <SelectValue placeholder="Select a verified email to display" />
                  <div className=''>
                    <MdArrowDropDown size={25} />
                  </div>
                </div>
              </SelectTrigger>
              <SelectContent className='w-80 bg-foreground border border-primary h-80 overflow-y-scroll scrollbar-thumb-primary scrollbar-thin scrollbar-thumb-rounded'>
                {fakeData.map((index) => {

                  const isFinal = index == fakeData.length

                  return (
                    <SelectItem key={index} value={`Item ${index}`} className={`border-b text-center border-gray-400 w-full ${isFinal ? 'border-none' : 'border-b'} p-1`}>Item {index}</SelectItem>
                  )
                })}
              </SelectContent>
            </Select>
          )} />
        <DropdownCause />
        <DropdownShop />
        <TextArea fieldName='Defeito' />
        <Button text='Enviar' type='submit' style={'w-full p-1'} />
      </form>
    </div>
  );
}

问题分析

  1. 错误传递props:将{...field}直接传给Radix UI Select,Radix的Select组件不兼容React Hook Form的field对象中的部分props(如value的传递逻辑),导致取值失效。
  2. 无效默认值:设置的defaultValue='312321'不存在于下拉选项中,组件无法识别该默认值。
  3. 状态脱节:未将field.value绑定到Select的value属性,导致组件选中状态与表单状态不同步。

修复后的代码

'use client';
import React from 'react';
import { useCurrentUser } from '@/hooks/useCurrentUser';
import { FormField } from '../ui/forms';
import { TextArea } from '../ui/forms/TextArea';
import DropdownCause from './DropdownCause';
import DropdownShop from './DropdownShops';
import Button from '../ui/Button';
import { DataPicker } from '../ui/forms/DataPicker';
import { useForm, Controller } from 'react-hook-form';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@radix-ui/react-select';
import DropdownNewTicket from './DropdownNewTicket';
import { MdArrowDropDown } from 'react-icons/md'
const fakeData = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]


export default function NewTicketForm() {

  const session = useCurrentUser();
  let name = session?.name ? session.name : ' ';

  const { handleSubmit, register, control } = useForm({
    mode: 'all',
    reValidateMode: 'onChange',
    // 统一在useForm中设置表单默认值
    defaultValues: {
      product: 'Item 1'
    }
    //resolver: zodResolver()
  });

  const createTicket = (data: any) => {
    console.log(data);
  };

  return (
    <div className='border-2 border-primary p-3 rounded justify-center self-center w-2/3 h-5/6 scrollbar-thumb-slate-700 scrollbar-thin scrollbar-thumb-rounded flex flex-col items-center overflow-y-scroll'>
      <form className='flex flex-col gap-6' onSubmit={handleSubmit(createTicket)}>
        <FormField fieldName='Nome' className='w-80 rounded border border-primary indent-1 text-lg' value={name} {...register('username')} />
        <DataPicker fieldName='Data da venda' {...register('sale-date')} />
        <Controller
          control={control}
          name="product"
          rules={{ required: true }}
          render={({ field }) => (
            // 仅绑定必要属性,避免传递不兼容props
            <Select 
              value={field.value} 
              onValueChange={field.onChange}
              onBlur={field.onBlur} // 可选:绑定失焦事件触发表单校验
            >
              <SelectTrigger className='w-80 h-fit rounded-t rounded-tl border border-primary text-lg flex' asChild>
                <div className='flex justify-center'>
                  <SelectValue placeholder="Selecione um produto" />
                  <div className=''>
                    <MdArrowDropDown size={25} />
                  </div>
                </div>
              </SelectTrigger>
              <SelectContent className='w-80 bg-foreground border border-primary h-80 overflow-y-scroll scrollbar-thumb-primary scrollbar-thin scrollbar-thumb-rounded'>
                {fakeData.map((index) => {
                  const isFinal = index === fakeData.length
                  return (
                    <SelectItem key={index} value={`Item ${index}`} className={`border-b text-center border-gray-400 w-full ${isFinal ? 'border-none' : 'border-b'} p-1`}>
                      Item {index}
                    </SelectItem>
                  )
                })}
              </SelectContent>
            </Select>
          )} />
        {/* 自定义Dropdown组件同样需要用Controller包裹并传递必要属性 */}
        <Controller
          control={control}
          name="cause"
          render={({ field }) => <DropdownCause value={field.value} onChange={field.onChange} />}
        />
        <Controller
          control={control}
          name="shop"
          render={({ field }) => <DropdownShop value={field.value} onChange={field.onChange} />}
        />
        <TextArea fieldName='Defeito' {...register('defect')} />
        <Button text='Enviar' type='submit' style={'w-full p-1'} />
      </form>
    </div>
  );
}

关键要点

  • 精准绑定属性:仅将field.value、field.onChange(可选field.onBlur)传递给Radix Select,避免直接spread不兼容的props。
  • 有效默认值:确保默认值存在于下拉选项中,建议在useForm的defaultValues中统一配置,而非组件内部单独设置。
  • 自定义组件处理:如果DropdownCause、DropdownShop是基于Radix Select封装的自定义组件,需在组件内部接收并绑定value和onChange属性,再用Controller包裹接入表单。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 08:25:56