如何自定义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;
核心说明
- 数据结构适配:将所有可选中的子选项和普通选项统一提供给AutoComplete的
options属性,确保组件能识别并跟踪这些选项的选中状态。 - 父选项与子选项分离:父选项仅负责展开/折叠子菜单,不参与选中逻辑;子选项完全继承AutoComplete的选中props,点击时自动触发
onChange。 - 状态同步:用
useRef跟踪父选项的展开状态,避免组件内部useState导致的状态隔离,同时通过setValue触发重绘来更新折叠面板状态。 - 选中状态正确映射:子选项的
selected状态直接绑定AutoComplete的value,确保显示正确的选中样式。
内容的提问来源于stack exchange,提问作者Nyi Nyi Hmue Aung
相关产品推荐
相关产品推荐

