You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在MUI的Select组件中嵌套div使用MenuItem而非直接子元素?

解决MUI Select带勾选+自定义头部/底部的问题

问题根源

  1. MUI Select组件要求MenuItem必须是它的直接子元素,嵌套div会破坏组件内部的事件传递和值关联逻辑,导致无法正确识别选项
  2. 点击复选框时event.target.value为undefined,是因为嵌套结构让事件无法正确传递到Select组件,无法获取到MenuItem的value值

两种可行解决方案

方案一:拆分Select与Menu,完全自定义下拉面板

直接用Select触发Menu弹出,手动控制Menu的显示隐藏,这样可以自由添加头部、底部,同时保证MenuItem的层级正确:

import { useState } from 'react';
import { Select, OutlinedInput, Menu, MenuItem, Checkbox, ListItemText, Typography, Box } from '@mui/material';

export default function CustomSelectWithHeaderFooter({ dataArray }) {
  const [personName, setPersonName] = useState([]);
  const [anchorEl, setAnchorEl] = useState(null);
  const open = Boolean(anchorEl);

  const handleOpen = (event) => {
    setAnchorEl(event.currentTarget);
  };

  const handleClose = () => {
    setAnchorEl(null);
  };

  const handleChange = (name) => {
    const currentIndex = personName.indexOf(name);
    const newSelected = [...personName];

    if (currentIndex === -1) {
      newSelected.push(name);
    } else {
      newSelected.splice(currentIndex, 1);
    }

    setPersonName(newSelected);
  };

  return (
    <div>
      <Select
        labelId="demo-multiple-checkbox-label"
        id="demo-multiple-checkbox"
        multiple
        value={personName}
        open={open}
        onOpen={handleOpen}
        onClose={handleClose}
        input={<OutlinedInput label="Tag" />}
        renderValue={(selected) => selected.join(', ')}
        displayEmpty
      >
        {/* 这里留空,下拉面板用自定义Menu实现 */}
      </Select>

      <Menu
        anchorEl={anchorEl}
        open={open}
        onClose={handleClose}
        PaperProps={{
          style: {
            maxHeight: 400,
            width: 250,
          },
        }}
      >
        {/* 自定义头部 */}
        <Box px={2} py={1} borderBottom={1} borderColor="divider">
          <Typography variant="subtitle2">Sticky header</Typography>
        </Box>

        {/* 选项列表 */}
        {dataArray.map((name) => (
          <MenuItem key={name} onClick={() => handleChange(name)}>
            <Checkbox checked={personName.indexOf(name) > -1} />
            <ListItemText primary={name} />
          </MenuItem>
        ))}

        {/* 自定义底部 */}
        <Box px={2} py={1} borderTop={1} borderColor="divider">
          <Typography variant="subtitle2">Sticky footer</Typography>
        </Box>
      </Menu>
    </div>
  );
}

方案二:通过Select的MenuProps自定义菜单内容(更简洁)

利用Select的MenuProps属性,直接给内部的Menu组件添加自定义头部和底部,同时保证MenuItem是Menu的直接子元素:

import { useState } from 'react';
import { Select, OutlinedInput, MenuItem, Checkbox, ListItemText, Typography, Box } from '@mui/material';

export default function CustomSelectWithHeaderFooter({ dataArray }) {
  const [personName, setPersonName] = useState([]);

  const handleChange = (event) => {
    // 手动处理多选逻辑,自定义结构需自行维护选中数组
    const { value } = event.target;
    setPersonName(typeof value === 'string' ? value.split(',') : value);
  };

  return (
    <Select
      labelId="demo-multiple-checkbox-label"
      id="demo-multiple-checkbox"
      multiple
      value={personName}
      onChange={handleChange}
      input={<OutlinedInput label="Tag" />}
      renderValue={(selected) => selected.join(', ')}
      MenuProps={{
        PaperProps: {
          children: (props) => (
            <div>
              {/* 自定义头部 */}
              <Box px={2} py={1} borderBottom={1} borderColor="divider">
                <Typography variant="subtitle2">Sticky header</Typography>
              </Box>
              {/* 保留原Menu的子元素(即MenuItem列表) */}
              {props.children}
              {/* 自定义底部 */}
              <Box px={2} py={1} borderTop={1} borderColor="divider">
                <Typography variant="subtitle2">Sticky footer</Typography>
              </Box>
            </div>
          ),
          style: {
            maxHeight: 400,
            width: 250,
          },
        },
      }}
    >
      {/* MenuItem直接作为Select的子元素,符合组件要求 */}
      {dataArray.map((name) => (
        <MenuItem key={name} value={name}>
          <Checkbox checked={personName.indexOf(name) > -1} />
          <ListItemText primary={name} />
        </MenuItem>
      ))}
    </Select>
  );
}

关键说明

  • 方案二的核心是通过MenuProps.PaperProps.children注入自定义结构,同时保留原有的props.children(即Select渲染的MenuItem列表),既满足Select对MenuItem层级的要求,又能添加自定义头部底部
  • 多选模式下,handleChange需要正确处理数组类型的value,避免出现undefined的情况

内容的提问来源于stack exchange,提问作者Exter

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 07:07:07