带有 MUI 自动完成、FreeSolo 和相关字段的 React Hook Form

React Hook Form with MUI Autocomplete, FreeSolo and dependent fields

我正在尝试从 this JSON file (here's the repo as well 中创建 ZipCode / City / State(意大利语 CAP / Città / Provincia)依赖字段。我正在使用 React Hook Form v7 和 MUI v5.4.4。我想使用带有 FreeSolo 道具的 MUI 自动完成组件来实现这 3 个字段,以便让用户在 JSON 列表中不存在的情况下插入自定义输入值。

我试图让它起作用,但它不起作用。我该如何实施?此外,自动完成组件的验证不起作用。

Here's the codesandbox that I wrote

您的代码中存在几个问题:

  • 您忘记将 rules 道具传递给您的 <Controller />
  • 当前选择的值将作为第二个参数传递给 <Autocomplete />onChange 处理程序
  • 您需要使用 RHF 的 watch 方法来对这 3 个相关字段的更改做出反应,并相应地过滤其他选择的选项
  • 您需要使用 flatMap 而不是 map 来映射邮政编码的选项,因为 option.cap 是一个数组
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);
            }}
          />
        );
      }}
    />
  );
}

更新

由于您有一个非常大的 json 文件,您有两个选项可以优化性能:

  • 通过 <Autocomplete />filterOptions 属性限制选项的数量 -> createFilterOptions 函数可以配置为设置限制
  • 在将选项传递给 <Autocomplete /> 之前,添加一个 useMemo 钩子用于筛选和映射您的选项,例如现在,在其他字段(firstNamelastNameaddress)的每个输入更改时,将重新计算选项