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

如何自定义MUI AutoComplete实现子菜单并解决onChange失效问题?

解决MUI AutoComplete子菜单选项onChange不触发问题

你的问题核心是自定义子菜单时,没有让AutoComplete的内置选中机制正常工作,导致切换子选项时组件内部状态不同步,onChange无法触发。下面是修正后的实现方案:

关键调整点

  • 父选项(带子菜单的项)仅作为展开/折叠触发器,不参与选中逻辑,避免干扰AutoComplete的默认事件。
  • 子选项完全绑定AutoComplete的内置选中逻辑,确保点击时触发onChange并同步组件状态。
  • 用ref跟踪子菜单展开状态,避免组件内部useState导致的状态隔离问题。

修正后的代码示例

import { useState, useRef } from 'react';
import { AutoComplete, MenuItem, List, Collapse, Typography, Box, KeyboardArrowDownIcon, TextField } from '@mui/material';

// 定义数据类型示例
interface Category {
  id: string;
  name: string;
}

interface Option {
  name: string;
  categories?: Category[];
}

const AutoCompleteWithSubmenu = () => {
  // 用ref跟踪每个父选项的展开状态,避免重绘影响
  const expandedRef = useRef<Record<string, boolean>>({});
  const [value, setValue] = useState<Category | null>(null);
  
  // 示例选项数据
  const options: Option[] = [
    { 
      name: "label410", 
      categories: [
        { id: "entry", name: "label410 (Entry)" },
        { id: "exit", name: "label410 (Exit)" }
      ]
    },
    { name: "普通选项1" },
    { name: "普通选项2" }
  ];

  // 处理父选项点击,切换展开状态
  const handleParentClick = (optionName: string) => {
    expandedRef.current[optionName] = !expandedRef.current[optionName];
    // 强制重绘更新折叠状态
    setValue({...value} as Category);
  };

  return (
    <AutoComplete
      value={value}
      onChange={(event, newValue) => {
        setValue(newValue);
        // 这里处理你的业务逻辑
        console.log("选中项变化:", newValue);
      }}
      // 把所有可选中的选项(子选项+普通选项)提取出来给AutoComplete
      options={[
        ...options.filter(opt => !opt.categories),
        ...options.flatMap(opt => opt.categories || [])
      ]}
      getOptionLabel={(option) => option.name}
      renderOption={(props, option, { selected }) => {
        // 判断当前选项是否属于某个父选项的子项
        const parentOption = options.find(opt => opt.categories?.includes(option as Category));
        if (parentOption) {
          // 子选项:完全传递AutoComplete的props,确保选中逻辑生效
          return (
            <MenuItem {...props} selected={selected} sx={{ pl: 6 }}>
              <Typography>{option.name}</Typography>
            </MenuItem>
          );
        } else {
          // 普通选项或父选项(父选项不参与选中)
          const isParent = !!options.find(opt => opt.name === option.name && opt.categories);
          if (isParent) {
            // 父选项:仅作为展开触发器,不传递选中相关props
            return (
              <Box onClick={() => handleParentClick(option.name)} sx={{ width: '100%' }}>
                <MenuItem 
                  sx={{ 
                    width: '100%', 
                    display: 'flex', 
                    justifyContent: 'space-between', 
                    alignItems: 'center' 
                  }}
                >
                  <Typography>{option.name}</Typography>
                  <KeyboardArrowDownIcon />
                </MenuItem>
                {/* 子菜单折叠面板 */}
                <Collapse in={expandedRef.current[option.name] || false}>
                  <List>
                    {options.find(opt => opt.name === option.name)?.categories?.map(category => (
                      <MenuItem 
                        key={category.id} 
                        {...props} // 传递AutoComplete的props给子选项
                        selected={value?.id === category.id}
                        sx={{ pl: 6 }}
                      >
                        <Typography>{category.name}</Typography>
                      </MenuItem>
                    ))}
                  </List>
                </Collapse>
              </Box>
            );
          } else {
            // 普通选项
            return (
              <MenuItem {...props} selected={selected}>
                <Typography>{option.name}</Typography>
              </MenuItem>
            );
          }
        }
      }}
      renderInput={(params) => <TextField {...params} label="带子菜单的AutoComplete" />}
    />
  );
};

export default AutoCompleteWithSubmenu;

核心说明

  1. 数据结构适配:将所有可选中的子选项和普通选项统一提供给AutoComplete的options属性,确保组件能识别并跟踪这些选项的选中状态。
  2. 父选项与子选项分离:父选项仅负责展开/折叠子菜单,不参与选中逻辑;子选项完全继承AutoComplete的选中props,点击时自动触发onChange。
  3. 状态同步:用useRef跟踪父选项的展开状态,避免组件内部useState导致的状态隔离,同时通过setValue触发重绘来更新折叠面板状态。
  4. 选中状态正确映射:子选项的selected状态直接绑定AutoComplete的value,确保显示正确的选中样式。

内容的提问来源于stack exchange,提问作者Nyi Nyi Hmue Aung

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 03:20:12