I have a form with quite a number of fields. Below just a part of those:
return (
<>
<FormProvider {...methods} >
<div className="mt-12">
<form onSubmit={handleSubmit(onSubmit)} action="#" method="POST" >
<Email/>
<Personal/>
<h2 className='font-semibold text-xl'>Geburtsdaten</h2>
<p className='mb-5 text-gray-400 text-sm'>Du musst mindestens 18 Jahre alt sein, um online einen Vertrag abzuschließen.</p>
<div className="sm:col-span-2 grid grid-cols-3 gap-4 mb-5">
<div className="mt-1">
<SelectBirthYear
years={years}
value={selectedYear}
onChange={setSelectedYear}/>
</div>
<div className="mt-1">
< SelectBirthMonth
startYear={startYear}
selectedYear={selectedYear}
months={months}
value={selectedMonth}
reducedMonths={reducedMonths}
onChange={monthSelect}/>
</div>
SelectBirthYear and SelectBirthMonth are the selects. So, when on Submit I console.log formData, I get the following:
{emailRequired: "[email protected]", nameRequired: "Test", surnameRequired: "Test", streetRequired: "xxxx", numberRequired: "123", …}
Basically all my selects get ignored.
I tried to register the select field, but the value comes undefined:
export const SelectBirthYear = ({ years, value, onChange }) => {
const {register} = useFormContext();
return (
<Listbox value={value} onChange={onChange} >
{({ open }) => (
<>
<Listbox.Label className="block text-sm font-medium text-gray-700 xs:text-xs">Geburtsjahr</Listbox.Label>
<div className="mt-1 relative">
<Listbox.Button className="bg-white relative w-full border border-gray-300 rounded-md shadow-sm pl-3 pr-10 py-2 text-left cursor-default focus:outline-none focus:ring-1 focus:ring-yellow-500 focus:border-yellow-500 sm:text-sm">
<span className="block truncate">{value.name}</span>
<span className="absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none">
<SelectorIcon className="h-5 w-5 text-gray-400" aria-hidden="true" />
</span>
</Listbox.Button>
<Transition
show={open}
as={Fragment}
leave="transition ease-in duration-100"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<Listbox.Options
static
{...register("birthYear")}
className="absolute z-10 mt-1 w-full bg-white shadow-lg max-h-60 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm"
>
{years.map((year, index) => (
<ListboxOption
key={index}
value={year}
date={year.name}
>
</ListboxOption>
))}
</Listbox.Options>
</Transition>
</div>
</>
)}
</Listbox>
)
}
emailRequired: "[email protected]", nameRequired: "Test", surnameRequired: "Test", birthYear: undefined
What am I doing wrong?
P.S. My Listbox.Option component :
import { CheckIcon } from '@heroicons/react/solid';
import { Listbox } from '@headlessui/react';
function classNames(...classes) {
return classes.filter(Boolean).join(' ')
}
export const ListboxOption = ({ value, date }) => {
return (
<Listbox.Option
className={({ active }) => classNames( active ? 'text-white bg-yellow-500' : 'text-gray-600', 'cursor-default select-none relative py-2 pl-3 pr-9')}
value={value}
>
{({ selected, active }) => (
<>
<span className={classNames(selected ? 'font-semibold' : 'font-normal', 'block truncate')}> {date} </span>
{selected ? (
<span className={classNames(active ? 'text-white' : 'text-yellow-500','absolute inset-y-0 right-0 flex items-center pr-4' )}>
<CheckIcon className="h-5 w-5" aria-hidden="true" />
</span>
) : null}
</>
)}
</Listbox.Option>
);
}
CodePudding user response:
The component Listbox
(is an external controlled components) isn't a native HTML input. In this case, to deal with this component you should use the wrapper component: Controller
, which gives your the freedom to use a custom register.
This is from React Hook Form:
React Hook Form embraces uncontrolled components and native HTML inputs, however, it's hard to avoid working with external controlled components such as React-Select, AntD and Material-UI. To make this simple, we provide a wrapper component: Controller to streamline the integration process while still giving you the freedom to use a custom register.
export const SelectBirthYear = ({ years }) => {
const { control } = useFormContext();
return (
<Controller // <=== You should use this wrapper
name="birthYear"
control={control}
render={({ field }) => (
<Listbox value={field.value} onChange={field.onChange}>
{({ open }) => (
<>
<Listbox.Label className="block text-sm font-medium text-gray-700 xs:text-xs">
Geburtsjahr
</Listbox.Label>
<div className="mt-1 relative">
<Listbox.Button className="bg-white relative w-full border border-gray-300 rounded-md shadow-sm pl-3 pr-10 py-2 text-left cursor-default focus:outline-none focus:ring-1 focus:ring-yellow-500 focus:border-yellow-500 sm:text-sm">
<span className="block truncate">{field.value.name}</span>
<span className="absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none">
<SelectorIcon
className="h-5 w-5 text-gray-400"
aria-hidden="true"
/>
</span>
</Listbox.Button>
<Transition
show={open}
as={Fragment}
leave="transition ease-in duration-100"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<Listbox.Options
static
className="absolute z-10 mt-1 w-full bg-white shadow-lg max-h-60 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm"
>
{years.map((year, index) => (
<ListboxOption
key={index}
value={year}
date={year.name}
></ListboxOption>
))}
</Listbox.Options>
</Transition>
</div>
</>
)}
</Listbox>
)}
/>
);
};
Here's a simple demo on how to use Listbox
with React Hook Form
: https://codesandbox.io/s/react-hook-form-v7-controller-forked-6t7qg?file=/src/MyListbox.js:173-180