Search code examples
reactjsmaterial-uirace-conditionconditional-rendering

Race Condition: Material-UI Select thinks value is out of range when performing conditional rendering to generate MenuItems


I'm using Material-UI to create some Selects for my dashboard. The MenuItems (options) in the Select are generated using conditional rendering. I use map to return MenuItems based on a set list of options from an array that is retrieved from a remote API. The value of the Select is also determined from a value retrieved from the remote API. Upon API response the defaultValue is updated to the actual value.

Here's a quick look at the relevant portion of the component:

            <Select
            MenuProps={{
                className: classes.selectMenu,
            }}
            classes={{
                select: classes.select,
            }}
            id={id}
            name={name}
            defaultValue={defaultValue}
            value={value}
            onChange={onChange}
        >
            <MenuItem
                disabled
                classes={{
                    root: classes.selectMenuItem,
                }}
                value={0}
            >
                {labelText}
            </MenuItem>
            {selectOptions.map(opt =>
                <MenuItem
                classes={{
                    root: classes.selectMenuItem,
                    selected: classes.selectMenuItemSelected,
                }}
                value={opt.IDX}
                key={`${name}${opt.IDX}`}
            >
                {opt.Name}
            </MenuItem>)}
        </Select>

Each one of the options has a IDX property and a name property. So for instance a list of users in the dropdown would show Bob, Sally, Jeff and the values would be 1,2,3,etc relative to Bob, Sally, and Jeff's ID numbers. As you can see there is a default option with value 0 specified and a defaultValue prop.

I receive the following warning in the JS console:

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 ''.
The available values are `0`.

which I can only assume is because React doesn't know about the conditionally rendered MenuItems until after they are rendered, and at that point it is possible that the remote API has already responded and the value of the Select component is already set to something besides the defaultValue.

This does not seem to affect code functionality in any way but it is a race condition and I don't like it. Is there a way to perform a check to ensure my component has all MenuItems available and their values set before setting the parent select component's value?

I was thinking about setting something like an isFullyProcessed boolean within the JSX somehow and then working with that.

EDIT:

Here is how I'm fetching my data on the parent page. The schedules and groups are the options data and the user is where the value comes from.

   useEffect(() => {
    axios.get(`${server}${uPath}/data`).then((response) => {
      const parsed = JSON.parse(response.data);
      setSchedules(parsed.schedules);
      setGroups(parsed.groups);
      setUser(parsed.user);

Solution

  • As was commented, this was solved by waiting to render until all data was retrieved from the API. Apparently, even though the API call for setting value and setting the menuoptions was done in the same function, because I use two separate pieces of state to store this info, it is possible for value to be set in a different render cycle than the menuoptions are rendered.

    I added an isLoading boolean and waited for the useeffect to return the data from the API and this resolved the issue. Quick tutorial here.