Material UI TreeView右键菜单操作时意外切换节点状态问题
解决Material UI TreeView右键菜单操作时节点意外展开/折叠的问题
问题描述
基于Material UI TreeView的富对象示例扩展右键菜单后,出现异常:点击右键菜单选项,或点击页面其他区域关闭菜单时,对应的TreeView节点会意外触发展开/折叠状态切换。预期操作右键菜单的过程中,不应影响节点的展开/折叠状态。
问题原因
事件冒泡导致点击菜单或关闭菜单的事件向上传递到TreeItem组件,触发了TreeItem默认的展开/折叠切换逻辑。
解决方案
通过阻止菜单相关事件的冒泡,避免事件传递到TreeItem组件,具体修改如下:
- 更新右键菜单的关闭处理函数,接收事件参数并阻止事件传播
- 为
Menu组件的onClose事件添加事件传播阻止逻辑 - 确保菜单项的点击事件也触发带阻止传播的关闭逻辑
修改后的完整代码
import * as React from "react"; import { Typography, Menu, MenuItem } from "@mui/material"; import TreeView from "@mui/lab/TreeView"; import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; import ChevronRightIcon from "@mui/icons-material/ChevronRight"; import TreeItem from "@mui/lab/TreeItem"; interface RenderTree { id: string; name: string; children?: readonly RenderTree[]; } const data: RenderTree = { id: "root", name: "Parent", children: [ { id: "1", name: "Child - 1" }, { id: "3", name: "Child - 3", children: [ { id: "4", name: "Child - 4" } ] } ] }; const NodeWithContextMenu = (props) => { const [contextMenu, setContextMenu] = React.useState<{ mouseX: number; mouseY: number; } | null>(null); const handleContextMenu = (event: React.MouseEvent) => { event.preventDefault(); setContextMenu( contextMenu === null ? { mouseX: event.clientX + 2, mouseY: event.clientY - 6 } : null ); }; const handleClose = (event: React.MouseEvent) => { // 阻止事件冒泡到TreeItem,避免触发展开/折叠 event.stopPropagation(); setContextMenu(null); }; return ( <div onContextMenu={handleContextMenu} style={{ cursor: "context-menu" }}> <Typography>{props.label}</Typography> <Menu open={contextMenu !== null} onClose={(event) => { // 点击外部关闭菜单时,阻止事件冒泡 event.stopPropagation(); handleClose(event); }} anchorReference="anchorPosition" anchorPosition={ contextMenu !== null ? { top: contextMenu.mouseY, left: contextMenu.mouseX } : undefined } > <MenuItem onClick={handleClose}>Copy</MenuItem> <MenuItem onClick={handleClose}>Print</MenuItem> <MenuItem onClick={handleClose}>Highlight</MenuItem> <MenuItem onClick={handleClose}>Email</MenuItem> </Menu> </div> ); }; export default function RichObjectTreeView() { const renderTree = (nodes: RenderTree) => ( <TreeItem key={nodes.id} nodeId={nodes.id} label={<NodeWithContextMenu label={nodes.name} />} > {Array.isArray(nodes.children) ? nodes.children.map((node) => renderTree(node)) : null} </TreeItem> ); return ( <TreeView aria-label="rich object" defaultCollapseIcon={<ExpandMoreIcon />} defaultExpanded={["root"]} defaultExpandIcon={<ChevronRightIcon />} sx={{ height: 110, flexGrow: 1, maxWidth: 400, overflowY: "auto" }} > {renderTree(data)} </TreeView> ); }
内容的提问来源于stack exchange,提问作者Sabir Hassan
相关产品推荐
相关产品推荐

