How to use MUI Select with react-hook-form?

23,565

Solution 1

Using Select component from Material-ui with react hook form need you to implement custom logic with a Controller https://react-hook-form.com/api#Controller

Here is a reusable component that will hopefully simplify the code to use that Select component in your app:

import FormControl from "@material-ui/core/FormControl";
import InputLabel from "@material-ui/core/InputLabel";
import Select from "@material-ui/core/Select";
import { Controller } from "react-hook-form";

const ReactHookFormSelect = ({
  name,
  label,
  control,
  defaultValue,
  children,
  ...props
}) => {
  const labelId = `${name}-label`;
  return (
    <FormControl {...props}>
      <InputLabel id={labelId}>{label}</InputLabel>
      <Controller
        as={
          <Select labelId={labelId} label={label}>
            {children}
          </Select>
        }
        name={name}
        control={control}
        defaultValue={defaultValue}
      />
    </FormControl>
  );
};
export default ReactHookFormSelect;

You can use it in your app like this:

           <ReactHookFormSelect
              id="numero_prestacao"
              name="numero_prestacao"
              className={classes.textField}
              label="Em quantas parcelas?"
              control={control}
              defaultValue={numero_prestacao || ""}
              variant="outlined"
              margin="normal"
            >
              <MenuItem value="">Escolha uma opção</MenuItem>
              <MenuItem value="3">03 parcelas</MenuItem>
              <MenuItem value="6">06 parcelas</MenuItem>
              <MenuItem value="9">09 parcelas</MenuItem>
              <MenuItem value="12">12 parcelas</MenuItem>
              <MenuItem value="16">16 parcelas</MenuItem>
              <MenuItem value="18">18 parcelas</MenuItem>
            </ReactHookFormSelect>

Here is your codeSandBox updated with this component for the selects in the Information form:

https://codesandbox.io/s/unit-multi-step-form-kgic4?file=/src/Register/Information.jsx:4406-5238

Solution 2

RHF v7 update

Below is a minimal code example of MUI Select in a RHF form:

const { formState, getValues, watch, register, handleSubmit } = useForm();
const { errors } = formState;
<TextField
  select
  fullWidth
  label="Select"
  defaultValue=''
  inputProps={register('currency', {
    required: 'Please enter currency',
  })}
  error={errors.currency}
  helperText={errors.currency?.message}
>
  {currencies.map((option) => (
    <MenuItem key={option.value} value={option.value}>
      {option.label}
    </MenuItem>
  ))}
</TextField>

Codesandbox Demo

Solution 3

Here my code that working, hope it can help, need to use setValue

  <TextField
    fullWidth
    inputRef={register({
      name: 'name',
    })}
    select
    onChange={e => setValue('name', e.target.value, true)}
    label={label}
    defaultValue={defaultValue}
  >
    {options.map((option) => (
      <MenuItem key={option.label} value={option.value}>
        {option.label}
      </MenuItem>
    ))}
  </TextField>

Here using native select, do not need setValue, but value alway string

<TextField
    fullWidth
    select
    SelectProps={{
      native: true,
      inputProps: { ref: register, name: 'name' }
    }}
    label={label}
    defaultValue={defaultValue}
  >
    {options.map((option) => (
      <option key={option.label} value={option.value}>
        {option.label}
      </option>
    ))}
  </TextField>

Solution 4

This is an example that uses Material-UI with React hook form. You need to add the validation in 'inputRef' prop of TextField. Also you need to add 'onChange' function to keep the state updated. 'shouldValidate' will trigger the validation.

  <TextField
    select
    name='city'
    inputRef={register({ required: true })}
    onChange={e => setValue('city', e.target.value, { shouldValidate: true })}
    label="City"
    defaultValue="">
    {cityList.map((option, index) => (
      <MenuItem key={index} value={option}>
        {option}
      </MenuItem>
    ))}
  </TextField>

  {errors.city && <ErrorText>City is required</ErrorText>}
Share:
23,565
YungHK
Author by

YungHK

Explorer and lifelong learner. Python | Finance | Processes

Updated on July 31, 2022

Comments

  • YungHK
    YungHK almost 2 years

    I've built a form in React using MUI and React Hook Form. I'm trying to create a custom TextField element that works as a Select Input. I would like it to be an uncontrolled component with a Ref prop. I've tried to pass the inputRef prop as the MUI and React Hook Form docs recommend but with no success.

                <TextField
                  id="id"
                  name="name"
                  select
                  native="true"
                  className={classes.textField}
                  label="label"
                  margin="normal"
                  variant="outlined"
                  inputRef={register({ required: "Choose one option" })}
                  error={!!errors.name}
                >
                  <MenuItem value="">Choose one option</MenuItem>
                  <MenuItem value="3">03</MenuItem>
                  <MenuItem value="6">06</MenuItem>
                  <MenuItem value="9">09</MenuItem>
                  <MenuItem value="12">12</MenuItem>
                  <MenuItem value="16">16</MenuItem>
                  <MenuItem value="18">18</MenuItem>
                </TextField>
    

    One thing that I've found is that if I use the native select with ref, it works just fine.

    Besides, I tried to change the inputRef prop to a SelectProps one but it didn't work too.

  • Ricardo Huertas
    Ricardo Huertas almost 4 years
    How do you handle the onChange event?
  • YungHK
    YungHK almost 4 years
    In this case we don't have onChange bc the input is handled by the DOM, i.e. an uncontrolled component.
  • abumalick
    abumalick almost 4 years
    Yes, as @YungHK said, react-hook-form works with uncontrolled inputs. If you need the value of an input in your form you can use watch: react-hook-form.com/api#watch
  • Paku
    Paku over 3 years
    To avoid the following error, you need to make sure you pass a valid defaultValue: Material-UI: A component is changing the uncontrolled value state of Select to be controlled. Elements should not switch from uncontrolled to controlled (or vice versa). Decide between using a controlled or uncontrolled Select element for the lifetime of the component.
  • Abdelhadi Abdo
    Abdelhadi Abdo over 3 years
    Yes Paku that's work with valid default value thanks guys
  • abumalick
    abumalick about 3 years
    the question is about using the select
  • Randall
    Randall over 2 years
    You demo have an error A component is changing an uncontrolled input to be controlled
  • NearHuscarl
    NearHuscarl over 2 years
    @Randall this is a terrible warning from MUI because RHF uses uncontrolled mode by default, in uncontrolled mode, value is undefined and the defaultValue is optional, but in this MUI component, you need to provide one explicitly for some reason. Anyway I fixed my answer, it should be working properly now.
  • Remy
    Remy over 2 years
    I'm trying to use this with mui v5 + typescript: Type '{ as: Element; name: string; control: any; defaultValue: any; }' is not assignable to type 'IntrinsicAttributes & { render: ({ field, fieldState, formState, }: { field: ControllerRenderProps<FieldValues, string>; fieldState: ControllerFieldState; formState: UseFormStateReturn<...>; }) => ReactElement<...>; } & UseControllerProps<...>'.
  • gowthz
    gowthz over 2 years
    What is the difference between this approach and the Controller component (accepted answer) approach ?