javascriptreactjsmaterial-ui

MUI React Dropdown prop passable filter


I am trying to create a filter setup that I can add to a Material-UI dropdown that I can pass via props of what specified value of specified variable so I can reused the filter on any of my dropdowns, so I can declare different variable and different values for each.

The API used to populate the Dropdown is returning a value set similar to this:

0: {id: 7, name: 'Time', isOnHold: 0}
1: {id: 8, name: 'Temperature', isOnHold: 1}

I would like to pass the variable as filterVar, and the value of that variable as filterVal so I can select different variable for different dropdowns. In the example below I want to filter out options where isOnHold = 1.

Here is what I have attempted:

As called from the form:

<Dropdown
    onChange={handleInputChange}
    label='Parameter'
    name='parameterId'
    value={values.parameterId}
    prompt='Select A Parameter...'
    url={paramUrl} 
    filterVar={'isOnHold'}
    filterVal={1}
/>

The Dropdown component:

import * as React from 'react';
import axios from 'axios';
import { FormControl, FormHelperText, InputLabel, Select, MenuItem } from '@material-ui/core';

function Dropdown(props) {
  const { filterVar, filterVal, label, name, onChange, prompt, value, url, ...other } = props;
  const [options, setOptions] = React.useState([]);

  React.useEffect(() => {
    const fetchData = async () => {
      const res = await axios.get(url);
      setOptions(res.data);
    };
    fetchData();
  }, []); 
 
  return(
    <FormControl >
        <InputLabel>{label}</InputLabel>
        <Select
           onChange={onChange}
           value={value} 
           name={name}
           {...other}
         >
         <MenuItem>{prompt}</MenuItem>
         {options.filter(option => (option.filterVar).includes(filterVal)).map((option, id) => (
           <MenuItem
              key={option.id}
              value={option.id}
           >
              {option.name}
           </MenuItem>
        ))}
      </Select>
   </FormControl>
  );
}

export default Dropdown;

As it right now, I can't get this to work, I keep getting an error of:

Uncaught TypeError: Cannot read properties of undefined (reading 'includes')

, so if anyone know how I can fix this, or had a better want to handle filtering the options, it would be greatly appreciated. Something likes passing option.isOnHold === 1.


Solution

  • Since you are sending the property name to the filterVar variable, you should use the property accessor with the Bracket notation ([<property name>]) instead of Dot notation as filterVar is not the property in the option object.

    Also note that from your attached data, the isOnHold is an integer instead of string. Hence, you should not use includes as it is a String function; instead, use the equal operator ===. Otherwise, you need to cast/convert isOnHold to string.

    Note that the <String>.includes("<substring>") should be used when checking the string value contains the substring.

    {options
      .filter((option) => option[filterVar] === filterVal)
      .map((option, id) => (
          <MenuItem key={option.id} value={option.id}>
            {option.name}
          </MenuItem>
      ))}
    

    Or

    {options
      .filter((option) => option[filterVar].toString().includes(filterVal))
      .map((option, id) => (
          <MenuItem key={option.id} value={option.id}>
            {option.name}
          </MenuItem>
      ))}
    

    Demo @ StackBlitz