带有 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 列表中不存在的情况下插入自定义输入值。
我试图让它起作用,但它不起作用。我该如何实施?此外,自动完成组件的验证不起作用。
您的代码中存在几个问题:
- 您忘记将
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
钩子用于筛选和映射您的选项,例如现在,在其他字段(firstName
、lastName
、address
)的每个输入更改时,将重新计算选项
我正在尝试从 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 列表中不存在的情况下插入自定义输入值。
我试图让它起作用,但它不起作用。我该如何实施?此外,自动完成组件的验证不起作用。
您的代码中存在几个问题:
- 您忘记将
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
钩子用于筛选和映射您的选项,例如现在,在其他字段(firstName
、lastName
、address
)的每个输入更改时,将重新计算选项