KaMZaTa
KaMZaTa

Reputation: 741

React Hook Form with MUI Autocomplete, FreeSolo and dependent fields

I'm trying to create ZipCode / City / State (in Italian CAP / Città / Provincia) dependent fields from this JSON file (here's the repo as well). I'm using React Hook Form v7 and MUI v5.4.4. I'd like to implement this 3 fields using MUI Autocomplete component with FreeSolo props in order to let the user to insert a custom input value if it's not present in the JSON list.

I tried to make it works but it doesn't. How can I implement that? Furthermore, the validation for the Autocomplete component doesn't work.

Here's the codesandbox that I wrote

Upvotes: 3

Views: 8834

Answers (1)

knoefel
knoefel

Reputation: 6979

There were several problems in your code:

  • you forget to pass the rules prop to your <Controller />
  • the current selected value will be passed as the second argument to <Autocomplete />'s onChange handler
  • you need to use RHF's watch method to react to changes of those 3 dependent fields and filter the options of the other selects accordingly
  • you need to use flatMap instead of map for the mapping of the options for postal codes, as option.cap is an array
export default function PersonalDetails() {
  const { watch } = useFormContext();

  const { postalCode, city, state } = watch("personalDetails");

  return (
    <Card variant="outlined" sx={{ width: 1 }}>
      <CardContent>
        <Grid container item spacing={2}>
           <Grid item xs={12} lg={3}>
            <SelectFree
              name="personalDetails.postalCode"
              label="ZIP (CAP)"
              options={options
                .filter((option) =>
                  city || state
                    ? option.nome === city || option.sigla === state
                    : option
                )
                .flatMap((option) => option.cap)}
              rules={{ required: "Richiesto" }}
            />
          </Grid>
          <Grid item xs={12} lg={10}>
            <SelectFree
              name="personalDetails.city"
              label="City (Città)"
              options={options
                .filter((option) =>
                  postalCode || state
                    ? option.cap.includes(postalCode) || option.sigla === state
                    : option
                )
                .map((option) => option.nome)}
              rules={{ required: "Richiesto" }}
            />
          </Grid>
          <Grid item xs={12} lg={2}>
            <SelectFree
              name="personalDetails.state"
              label="State (Sigla)"
              options={options
                .filter((option) =>
                  city || postalCode
                    ? option.nome === city || option.cap.includes(postalCode)
                    : option
                )
                .map((option) => option.sigla)}
              rules={{ required: "Richiesto" }}
            />
          </Grid>
        </Grid>
      </CardContent>
    </Card>
  );
}
export default function SelectFree({
  name,
  rules,
  options,
  getOptionLabel,
  ...rest
}) {
  const { control } = useFormContext();

  return (
    <Controller
      name={name}
      control={control}
      rules={rules}
      defaultValue={null}
      render={({
        field: { ref, ...field },
        fieldState: { error, invalid }
      }) => {
        return (
          <Autocomplete
            {...field}
            freeSolo
            handleHomeEndKeys
            options={options}
            getOptionLabel={getOptionLabel}
            renderInput={(params) => (
              <TextField
                {...params}
                {...rest}
                inputRef={ref}
                error={invalid}
                helperText={error?.message}
              />
            )}
            onChange={(e, value) => field.onChange(value)}
            onInputChange={(_, data) => {
              if (data) field.onChange(data);
            }}
          />
        );
      }}
    />
  );
}

Edit priceless-hamilton-pdxlww

UPDATE

As you have a very large json file you have two options where you can optimise performance:

  • limit the amount of options via the filterOptions prop of <Autocomplete /> -> the createFilterOptions function can be configured to set a limit
  • add a useMemo hook for the filtering and mapping of your options before passing them to the <Autocomplete />, e.g. right now on every input change for the other fields (firstName, lastName, address) the options will be recomputed

Edit trusting-franklin-9rqbls

Upvotes: 5

Related Questions