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

React Bootstrap嵌套菜单激活项默认不展开问题求助

解决React Bootstrap嵌套菜单默认展开激活页面父菜单的问题

你的核心需求是让当前激活页面对应的所有父折叠菜单默认展开,下面是具体的修改方案:

1. 修复子菜单激活检查逻辑(支持多层嵌套)

原hasActiveChild仅检查直接子项,多层嵌套场景下会失效,改成递归检查所有后代路由:

const hasActiveChild = (currentItem) => {
  // 检查当前项的直接子路由是否匹配
  const hasDirectActiveChild = currentItem.child.some(child => child.url === currentRoute);
  // 递归检查子项的后代路由是否匹配
  const hasNestedActiveChild = currentItem.child.some(child => child.child && hasActiveChild(child));
  return hasDirectActiveChild || hasNestedActiveChild;
};

2. 初始化展开状态,页面加载时直接展开需要的菜单

把open的初始状态从全false,改成根据菜单是否包含激活子项自动设置:

// 初始状态:包含激活子项的父菜单默认展开
const initialOpenState = menu.map(item => item.child && hasActiveChild(item));
const [open, setOpen] = useState(initialOpenState);

3. 修改useEffect,路由或菜单变化时自动更新展开状态

原useEffect仅处理菜单长度变化,现在改成监听路由和菜单数据变化,自动调整展开状态的同时保留用户手动操作的状态:

useEffect(() => {
  setOpen(prevOpen => prevOpen.map((isOpen, index) => {
    const menuItem = menu[index];
    // 仅当当前是父菜单且包含激活子项时强制展开,其他情况保留用户手动设置的状态
    return menuItem.child && hasActiveChild(menuItem) ? true : isOpen;
  }));
}, [currentRoute, menu]);

完整修改后的renderMenu代码

const renderMenu = (menu) => {
  const router = useRouter();
  const currentRoute = router.pathname;

  const hasActiveChild = (currentItem) => {
    const hasDirectActiveChild = currentItem.child.some(child => child.url === currentRoute);
    const hasNestedActiveChild = currentItem.child.some(child => child.child && hasActiveChild(child));
    return hasDirectActiveChild || hasNestedActiveChild;
  };

  // 初始状态:包含激活子项的父菜单默认展开
  const initialOpenState = menu.map(item => item.child && hasActiveChild(item));
  const [open, setOpen] = useState(initialOpenState);

  useEffect(() => {
    setOpen(prevOpen => prevOpen.map((isOpen, index) => {
      const menuItem = menu[index];
      return menuItem.child && hasActiveChild(menuItem) ? true : isOpen;
    }));
  }, [currentRoute, menu]);

  return menu.map((item, index) => (
    <Nav.Item className={item.child && "dropdown"} key={index}>
      {item.url ?
        <Nav.Link href={item.url} className={(currentRoute === item.url) && "active"}>
          <span>{item.title}</span>
        </Nav.Link>
      :
        <Nav.Link className={hasActiveChild(item) && "active"} onClick={() => {
          setOpen((open) => open.map((o, i) => {
            if (i === index) return !o;
            return o;
          }));
        }} aria-controls={`collapse${capitalizeEachWord(item.title)}`} aria-expanded={open[index]} data-bs-toggle="collapse">
          <span>{item.title}</span>
        </Nav.Link>
      }
      {item.child && (
        <Collapse in={open[index]} key={index} data-open={open[index]}>
          <div id={`collapse${capitalizeEachWord(item.title)}`}>
            <div className="nav flex-column">
              {renderMenu(item.child)}
            </div>
          </div>
        </Collapse>
      )}
    </Nav.Item>
  ))
};

关键说明

  • 递归检查:确保多层嵌套的子页面激活时,所有层级的父菜单都能自动展开
  • 状态保留:用户手动折叠/展开菜单后,非激活路径的菜单状态会被保留,仅激活路径的父菜单会强制展开
  • 依赖优化:useEffect仅监听currentRoute和menu变化,避免不必要的重渲染和无限循环

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 18:06:40