Reputation: 741
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
Reputation: 6979
There were several problems in your code:
rules
prop to your <Controller />
<Autocomplete />
's onChange
handlerwatch
method to react to changes of those 3 dependent fields and filter the options of the other selects accordinglyflatMap
instead of map
for the mapping of the options for postal codes, as option.cap
is an arrayexport 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);
}}
/>
);
}}
/>
);
}
UPDATE
As you have a very large json file you have two options where you can optimise performance:
filterOptions
prop of <Autocomplete />
-> the createFilterOptions
function can be configured to set a limituseMemo
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 recomputedUpvotes: 5