Dynamic Dependent Drop-Down Lists with MUI
Tags: javascript, react
Published: 2020-09-19 20:20:48

In my opinion, one of the most important factors that separates a polished app from an unpolished app, in terms of UX, is its methods of user input. When developing some of your first apps, you will likely run into many instances in which you will be tempted to use a simple input form that allows a user to type anything that they want, when there are actually only a limited number of specific things that they should be able to choose from—for example, when submitting a query to an API or database. Enter APIs and the Autocomplete component from Material UI, in conjunction with some other techniques we'll be going over in this blog.

Material UI is a whole separate topic which you can learn about here: https://material-ui.com/getting-started/installation/, but essentially it is a styling library with a lot of aesthetically pleasing modular components that are very easy to set up—Autocomplete, of course, being one of them. 

Let's say you want to set up a car selection input screen broken down by year, make, and model—a common scenario where dynamic dependent drop-down lists may be needed. It needs to be dynamic because car models change every year—so there will never cease to be new years and models being added to this list—and dependent because the makes that appear are dependent on the year that is selected, and the models are dependent on the make selected. Fortunately, there are a lot of APIs out there specifically for seeding lists like this. For the car example, fueleconomy.gov offers an API for this exact use case. 

When the selection screen is first loaded, we will need to first generate the top level of the menus—in this case, the Year selection. For this example, in React, we can use `useEffect()` (or `componentDidMount()`) for this.

```javascript
const [car, setCar] = useState({});
const [years, setYears] = useState([]);
const [makes, setMakes] = useState([]);
const [models, setModels] = useState([]);

useEffect(() => {
  axios.get('https://www.fueleconomy.gov/ws/rest/vehicle/menu/year')
    .then(({ data }) => {
      setYears(data.menuItem.map((item) => item.value));
    });
}, []);
```
As you can see above, we are also using `useState()` to hold the values for each list, as well as the car that will have been selected after the third drop-down menu is fulfilled. The GET request above will retrieve a list of all years and place them in the `years` array. So now that we have the years populated, we will need to display them as a list.

As I mentioned, the Material UI set up should be fairly simple, as well as the Autocomplete boxes. Info for setting up and importing the Autocomplete boxes here: https://material-ui.com/api/autocomplete/

```javascript
<Autocomplete
  onChange={(_, year) => getMakes(year)}
  options={years}
  renderInput={(params) => (
    <TextField {...params} label="Year" variant="outlined" />
  )}
/>
```

Let's break this down line by line. Within the Autocomplete component, we first have:

**onChange** - takes a callback function whose second parameter is the actual item that was selected. So here, when a year is selected, it will be passed to another function we will create called `getMakes()`, so makes specific to that year will be generated. 
**options** - simply points to the array that holds options for the menu.
**renderInput** - takes a callback function which generates the actual typing input box—in this case, the MUI TextField.

Now let's look at the aforementioned `getMakes()` function that will need to be created.

```javascript
const getMakes = (year) => {
  setModels([]);
  setCar({ year, make: '', model: '' });
  axios.get(`https://www.fueleconomy.gov/ws/rest/vehicle/menu/make?year=${year}`)
    .then(({ data }) => {
      setMakes(data.menuItem.map((item) => item.value));
    });
};
```

As you can see, this function will first reset the `models` array in case anything had previously been selected by the user. Then the car year is saved, and another endpoint from fueleconomy.gov for getting makes from a selected year is used to populate the makes array.

Then we have the selection box for makes.

```javascript
<Autocomplete
  onChange={(_, model) => getModels(model)}
  disabled={!makes.length}
  key={makes}
  options={makes}
  renderInput={(params) => (
    <TextField {...params} label="Make" variant="outlined" />
  )}
/>
```
The only difference to note between this and the last selection box, is that since this drop-down list is dependent on the one above it, it should be disabled until there are options to select, and it should change if its options change. That is why we have added the following props:

**disabled** - a simple boolean. In this case, with it being set to `!makes.length`, `disabled` will be true if the makes array is empty.
**key** - in this case, is being used just to reset the selection and items in the drop-down menu if a new set of makes is retrieved from the API as a result of changing the year.

Setting up the `getModels()` function and its drop down menu should be essentially the same as the previous. In conclusion, Autocomplete in Material UI can be a super easy method for setting up dynamic dependent drop down lists. There are a lot of other cases this can be used for, like city/state selection screens, in which case you could skip the entire API retrieval method and set up a simple static array for your selection options, since cities and states do not change very often. I hope this demo has been in some way useful to you and I wish you the best of luck in your endeavors. 

Thanks!