I want to create a form with two MUI Select
components. Conference and division are inter-related. Their relationships are described below.
-
There is a
conference
corresponding to eachdivision
, as shown in theteams
array. When adivision
is selected, the correspondingconference
will be automatically selected. -
There are 3 divisions in each
conference
. When aconference
is selected, thedivisions
list will be filtered to include divisions corresponding to that conference, e.g. ifWestern
is selected, the divisions list only includesNorthWest
,Pacific
andSouthWest
.
const teams = [
{
"conference": "Eastern",
"division": "Atlantic"
},
{
"conference": "Eastern",
"division": "Central"
},
{
"conference": "Eastern",
"division": "Southeast"
},
{
"conference": "Western",
"division": "Northwest"
},
{
"conference": "Western",
"division": "Pacific"
},
{
"conference": "Western",
"division": "Southwest"
}
]
export default function Demo() {
const [divisions, setDivisions] = useState([...new Set(teams.map(obj => obj.division))]);
const { control, formState: {errors} } = useForm({
defaultValues: {
conference: "",
division: ""
}
})
const conference = useWatch({
control,
name: "conference"
})
const division = useWatch({
control,
name: "division"
})
const handleConferenceChange = division => teams.find(obj => obj.division === division)?.conference
const handleDivisionChange = conference => teams.filter(obj => obj.conference === conference).map(item => item.division)
return (
<form>
<Grid container>
<Grid xs={6}>
<FormControl fullWidth required error={!!errors.conference}>
<InputLabel shrink sx={{".MuiInputLabel-asterisk": {color: "error.main"}}}>Conference</InputLabel>
<Controller
name="conference"
control={control}
rules={{required: "This field is required."}}
render={({ field}) => (
<Select
{...field}
displayEmpty
label="Conference"
renderValue={value => value ? value : <InputLabel>-- Select --</InputLabel>}
>
{[...new Set(teams.map(obj => obj.conference))].map((conference, index) => <MenuItem key={index} value={conference}>{conference}</MenuItem>)}
</Select>
)}
/>
{errors.conference && <FormHelperText>{errors.conference?.message}</FormHelperText>}
</FormControl>
</Grid>
<Grid xs={6}>
<FormControl fullWidth required error={!!errors.division}>
<InputLabel shrink sx={{".MuiInputLabel-asterisk": {color: "error.main"}}}>Conference</InputLabel>
<Controller
name="division"
control={control}
rules={{required: "This field is required."}}
render={({ field}) => (
<Select
{...field}
displayEmpty
label="division"
renderValue={value => value ? value : <InputLabel>-- Select --</InputLabel>}
>
{divisions.map((division, index) => <MenuItem key={index} value={division}>{division}</MenuItem>)}
</Select>
)}
/>
{errors.division && <FormHelperText>{errors.division?.message}</FormHelperText>}
</FormControl>
</Grid>
</Grid>
</form>
)
}
Normally react-hook-form will handle the onChange
event handler of the Select
component so that there is no need to specify the onChange
props. However, in my case since conference
and division
are inter-related, how can I use the handleConferenceChange
function in the Select
component of conference
and use the handleDivisionChange
function in the Select
component of division
? I’ve studied the react-hook-form documentation and searched some tutorials on YouTube. None of them cover my use case. Could someone please teach me how my use case can be implemented using react-hook-form?