最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - MUI Select component with custom children item - Stack Overflow

programmeradmin0浏览0评论

I'm trying to create a Select component with a series of custom items that are mapped through a list. Each item has a specific type and, based on that type, the menu item will have a certain MUI icon. I created a specific component for managing the whole Select component and another specific component to display each item, its value and its icon. The problem is that the onChange function is not triggered whenever I click on one of the items. What could the problem be?

I'm attaching a code of what I'm doing.

const myTypes = {
  TYPE_1: "Type 1",
  TYPE_2: "Type 2",
  TYPE_3: "Type 3",
};

function TypeSelect(props) {
  const classes = useStyles();
  const [state, setState] = useState(myTypes.TYPE_1);

  const onChangeType = (e) => {
    setState(e.target.value);
  };

  return (
    <Select
      id="select-type"
      value={state}
      onChange={onChangeType}
    >
      {Object.keys(myTypes).map((type) => (
        <TypeSelectMenuItem
          type={myTypes[type]}
          key={type}
        />
      ))}
    </Select>
  );
}

function TypeSelectMenuItem({ type }) {
  const renderIcon = () => {
    switch (type) {
      case myTypes.TYPE_1:
        return <ShortTextIcon />; // Material-UI icon
      case myTypes.TYPE_2:
        return <SubjectIcon />; // Material-UI icon
      case myTypes.TYPE_3:
        return <RadioButtonCheckedIcon />; // Material-UI icon
      default:
        return <Fragment />;
    }
  };

  return (
    <MenuItem value={type} >
      <ListItemIcon>{renderIcon()}</ListItemIcon>
      <ListItemText primary={type} />
    </MenuItem>
  );
}

I'm trying to create a Select component with a series of custom items that are mapped through a list. Each item has a specific type and, based on that type, the menu item will have a certain MUI icon. I created a specific component for managing the whole Select component and another specific component to display each item, its value and its icon. The problem is that the onChange function is not triggered whenever I click on one of the items. What could the problem be?

I'm attaching a code of what I'm doing.

const myTypes = {
  TYPE_1: "Type 1",
  TYPE_2: "Type 2",
  TYPE_3: "Type 3",
};

function TypeSelect(props) {
  const classes = useStyles();
  const [state, setState] = useState(myTypes.TYPE_1);

  const onChangeType = (e) => {
    setState(e.target.value);
  };

  return (
    <Select
      id="select-type"
      value={state}
      onChange={onChangeType}
    >
      {Object.keys(myTypes).map((type) => (
        <TypeSelectMenuItem
          type={myTypes[type]}
          key={type}
        />
      ))}
    </Select>
  );
}

function TypeSelectMenuItem({ type }) {
  const renderIcon = () => {
    switch (type) {
      case myTypes.TYPE_1:
        return <ShortTextIcon />; // Material-UI icon
      case myTypes.TYPE_2:
        return <SubjectIcon />; // Material-UI icon
      case myTypes.TYPE_3:
        return <RadioButtonCheckedIcon />; // Material-UI icon
      default:
        return <Fragment />;
    }
  };

  return (
    <MenuItem value={type} >
      <ListItemIcon>{renderIcon()}</ListItemIcon>
      <ListItemText primary={type} />
    </MenuItem>
  );
}
Share Improve this question edited Nov 1, 2021 at 2:16 NearHuscarl 81.4k22 gold badges318 silver badges280 bronze badges asked Apr 4, 2021 at 16:14 FsannaFsanna 3771 gold badge5 silver badges13 bronze badges 1
  • Can you paste the import statements too. – dileep nandanam Commented Apr 5, 2021 at 1:10
Add a comment  | 

1 Answer 1

Reset to default 16

According to Material-UI docs, this is how you use the MenuItem component to render Select's options:

<Select value={age} onChange={handleChange}>
  <MenuItem value={10}>Ten</MenuItem>
  <MenuItem value={20}>Twenty</MenuItem>
  <MenuItem value={30}>Thirty</MenuItem>
</Select>

When you want to pass a custom component instead of MenuItem as Select children item. For example TypeSelectMenuItem, you need to do the following things:

1. Pass all props to your custom item component

SelectInput will clone every child and attach additional event handlers as extra props behind the scene, so make sure to pass all of them down using spread operator:

const TypeSelectMenuItem = (props) => {
  return (
    <MenuItem {...props}>
      {...}
    </MenuItem>
  );
};
<Select value={state} onChange={onChangeType}>
  {Object.keys(myTypes).map((type) => (
    <TypeSelectMenuItem value={myTypes[type]} key={type}>
      {myTypes[type]}
    </TypeSelectMenuItem>
  ))}
</Select>

2. Pass the option value to the value props

Do not invent a new props name like what you did here:

<TypeSelectMenuItem type={myTypes[type]} key={type} />

The correct way is to pass to value instead of type props:

<TypeSelectMenuItem value={myTypes[type]} key={type} />

The reason is because internally, SelectInput references props.value instead of your props.type. See this and this. Using anything other than props.value will lead to unexpected behaviors.

3. Reference data-value instead of value in your custom component

When cloning item elements, SelectInput set the value props to undefined and utilize data-value props instead, So you need to use data-value props internally:

const TypeSelectMenuItem = (props) => {
  // when passing as children item of Select. props.value is set to undefined.
  // Use props['data-value'] instead.
  return (
    <MenuItem {...props}>
      {...}
      <ListItemText primary={props["data-value"]} />
    </MenuItem>
  );
};

4. Pass children to your custom item component to render selected value

SelectInput uses children props to render selected value, so you need to make sure to pass something as children to display:

const TypeSelectMenuItem = (props) => {
  return (
    <MenuItem {...props}>
      // this is children of MenuItem, so it is not displayed as selected value
      <ListItemIcon>{renderIcon()}</ListItemIcon>
      <ListItemText primary={props["data-value"]} />
    </MenuItem>
  );
};
<Select value={state} onChange={onChangeType}>
  {Object.keys(myTypes).map((type) => (
    <TypeSelectMenuItem value={myTypes[type]} key={type}>
      // this is children of TypeSelectMenuItem, so it is displayed as selected value
      {myTypes[type]}
    </TypeSelectMenuItem>
  ))}
</Select>

Live Demo

发布评论

评论列表(0)

  1. 暂无评论