I'm currently running into this issue where I have one select field that is dependent on the other one.
MUI: You have provided an out-of-range value `1` for the select component.
Consider providing a value that matches one of the available options or ''.
Here are my two fields
<Select
labelId="originEntityLabel"
id="originEntityField"
displayEmpty
defaultValue={entityForm.originEntity?.name ?? ""}
label="Entity"
onChange={(event: SelectChangeEvent) => onFieldChangeEntity(event.target.value, "entity")}
>
<MenuItem key="none" value="">
--None--
</MenuItem>
{entityList.map((entity) => {
return (
<MenuItem key={entity.name} value={entity.name}>
{entity.name}
</MenuItem>
)
})}
</Select>
And here's the Select field that I'm having an issue with.
<Select
labelId="startTierFieldLabel"
id="startTierField"
defaultValue={String(entityForm.startTier) ?? ""}
label="Start Tier"
onChange={(event: SelectChangeEvent) => onFieldChange(event.target.value, "tier")}
>
{tierRange && tierRange.map((tier) => {
return (
<MenuItem key={tier} value={tier}>
Tier {tier}
</MenuItem>
)
})}
</Select>
The tierRange is controlled via the state
const [tierRange, setTierRange] = useState<number[]>(range(0, 11));
Here are my two on change methods
const onFieldChange = (value: any, field: string) => {
const currEntity = entityForm as Entity;
currEntity[field as keyof Entity] = value as never;
console.log('field change', currEntity);
setEntityForm(currEntity);
}
const onFieldChangeEntity = (value: any, field: string) => {
console.log('field change entity', value);
const entityLookup = entityList.find(entity => entity.name === value);
const currEntity = entityForm as Entity;
const minTier = entityLookup ? entityLookup.startTier + 1 : 0;
currEntity.originEntity = entityLookup;
currEntity.startTier = Math.max(currEntity.startTier, minTier);
setEntityForm(cloneDeep(currEntity));
setTierRange(range(minTier, 11));
}
I'm not sure why this error comes up. When I try to log the information, the currEntity has the correct startTier, but I'm not sure why it's not reflecting correctly in the form.
I've also tried creating an entirely separate useEffect on the entityForm state, that then updates the tierRange
useEffect(() => {
console.log('in use effect entity form');
const minTier = entityForm.originEntity ? entityForm.originEntity.startTier + 1 : 0;
setTierRange(range(minTier, 11));
}, [entityForm]);
but that also didn't help the situation. I'm relatively new to typescript and mui, so I'm not sure what might be causing this. (Also, I am open to a better way of doing what I'm attempting - basically having 1 field have dependent values on another field, it would be great to know as well.)
TL; DR: use the value
prop of your 2nd <Select>
to turn it as a controlled Component, and reset it when its options are changing (at least when, as a result, its current value
is no longer part of its new options).
<Select>
(label="Start Tier"
) has some value
(initially from its defaultValue
, or its 1st option value from tierRange
)<Select>
(label="Entity"
), firing its onChange
callback, here onFieldChangeEntity
, which changes the tierRange
options of the 2nd Select, as well as the entityForm.startTier
defaultValue
prop of the 2nd Select, hence it affects its value
only initiallyvalue
of the 2nd Select is no longer be part of its options, you get your out-of-range errorIn step 2, you need to reset the value (not just the defaultValue) of the 2nd Select at the same time as you change its tierRange
options, hence it must be a controlled Component. At least if its current value is no longer part of its new options (but you may choose to reset it in all cases):
// value of 2nd Select (start tier)
const [selectedTier, setSelectedTier] = useState(String(entityForm.startTier) ?? tierRange[0]);
// onChange callback of the 2nd Select
const onFieldChange = (value: any, field: string) => {
// etc.
setSelectedTier(value); // We now have to explicitly control the Select value
}
// onChange callback of 1st Select (entity)
const onFieldChangeEntity = (value: any, field: string) => {
// etc.
const newTierRange = range(minTier, 11);
setTierRange(newTierRange);
// Reset the value of the 2nd Select (start tier),
// at least if its current value is no longer in tierRange
if (!newTierRange.includes(selectedTier)) {
setSelectedTier(minTier); // or currEntity.startTier
}
}
<Select
value={selectedTier} // Use the Select value prop to turn it into a controlled Component
label="Start Tier"
onChange={(event: SelectChangeEvent) => onFieldChange(event.target.value, "tier")}
>
{/* tierRange options */}
</Select>