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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 18:12:37