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

React中如何实现点击打开一个菜单时自动关闭其他已展开菜单

解决方案

问题根因

展开状态分散在每个MenuCard组件内部独立维护,组件之间状态不互通,无法实现点击一个菜单时同步关闭其他菜单的互斥逻辑。之前为每个菜单单独声明多个useState的方案也不可行,因为每个组件实例的状态都是独立的,修改当前组件的状态无法影响其他组件。

改造步骤

1. 改造父组件 Dashboard.tsx

将展开状态提升到父组件统一管理,新增activeMenu状态存储当前展开的菜单唯一标识(这里直接用菜单title即可,也可以新增独立id字段),并将状态和修改方法通过props传递给子组件:

// Dashboard.tsx
// 新增激活菜单状态
const [activeMenu, setActiveMenu] = React.useState<string | null>(null)

setMenuList([
        {
          title: "Thermal Comfort",
          icon: thermal,
          decorator: new ManekinDecorator(IModelApp.viewManager.selectedView!),
          tooltip: "Thermal Comfort",
        },
        {
          title: "Surface Plots",
          icon: surfacePlot,
          decorator: null,
          tooltip: "Surface Plots",
        },
        {
          title: "Contour Plots",
          icon: contour,
          decorator: null,
          tooltip: "Contour Plots",
        },
        {
          title: "Comfort Cloud",
          icon: comfortCloud,
          decorator: new ComfortDecorator(),
          tooltip: "Comfort Cloud",
        },
        {
          title: "Flowlines",
          icon: flowline,
          //decorator: getFlowLineDecorator(),
          decorator: null,
          tooltip: "FlowLines",
        },
      ]);
    } else {
      setMenuList([]);
      setIsDropDownVisibal(false);
    }
  }, [viewPort]);

 <div className="menu">
            <div style={{ overflowY: "scroll", width: "inherit" }}>
              {menuList.map((menu) => (
                <MenuCard
                  key={menu.title}
                  menu={menu.title}
                  icon={menu.icon}
                  decorator={menu.decorator}
                  tooltip={menu.tooltip}
                  // 新增两个props传递给子组件
                  activeMenu={activeMenu}
                  setActiveMenu={setActiveMenu}
                />
              ))}
            </div>
          </div>

2. 改造子组件 MenuCard.tsx

删除内部维护的所有展开相关状态,直接通过父组件传递的activeMenu判断当前菜单是否需要展开:

// MenuCard.tsx
const MenuCard = (props: any) => {
  const [toggle, setToggle] = React.useState(true);
  const { menu, icon, tooltip, activeMenu, setActiveMenu } = props;

  const onClick = () => {
    // 互斥展开逻辑:点击的菜单和当前激活的一致就收起,否则设为当前激活菜单
    setActiveMenu(activeMenu === menu ? null : menu)

    // 原有decorator逻辑保持不变
    if (props.decorator !== null) {
      if (toggle) {
        IModelApp.viewManager.decorators.forEach((decorator) => {
          IModelApp.viewManager.dropDecorator(decorator);
        });

        IModelApp.viewManager.addDecorator(props.decorator);
      } else IModelApp.viewManager.dropDecorator(props.decorator);
    }
    setToggle(!toggle);
  };

  return (
    <div>
      <div className="mainMenu" onClick={onClick}>
        <div className="icon">
          <img src={icon} alt="icon" className="menuIcon" />
        </div>
        <div className="menuTitle">
          <p className="title">{menu}</p>
        </div>
        <InfoTooltip tooltipText={tooltip} />
      </div>
      {/* 直接通过父组件传的activeMenu判断是否展开当前菜单 */}
      {activeMenu === menu && (
        <GetExpandedMenu menuName={menu} decorator={props.decorator} />
      )}
    </div>
  );
};

export default MenuCard;

优化说明

如果不需要支持点击当前展开的菜单收起,只需要把setActiveMenu(activeMenu === menu ? null : menu)修改为setActiveMenu(menu)即可。GetExpandedMenu组件无需做任何修改即可正常运行。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 12:15:04