Home > Mobile >  React-hook-form 'ignores' selects
React-hook-form 'ignores' selects

Time:10-11

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

  • Related