NextJS使用OutsideClickHandler时汉堡下拉菜单链接失效
问题现象
- 在NextJS项目中开发NavBar导航栏组件:宽屏模式下导航链接沿页面顶部排布,移动端通过汉堡按钮触发下拉侧边菜单,未添加外部点击监听逻辑时,所有导航链接跳转功能均运行正常
- 引入
react-outside-click-handler依赖实现点击菜单外部区域自动关闭下拉菜单的效果后,侧边下拉菜单内的导航链接完全失效:点击链接时会被判定为外部点击,直接触发菜单关闭逻辑,无法完成路由跳转 - 初步排查推测问题出在
OutsideClickHandler的包裹范围:当前仅包裹了汉堡图标元素,展开的侧边菜单不在点击监听的豁免范围内;曾考虑将OutsideClickHandler绑定到SideMenu组件上,但菜单的开关状态逻辑统一写在NavBar组件中,不清楚如何调整代码结构。
相关问题代码
未接入OutsideClickHandler的NavBar.js(侧边菜单链接可正常工作)
import { useState } from "react"; import Link from "next/link"; import styles from "../NavBar/NavBar.module.css"; import ButtonBase from "../button/buttonPrimary"; import SideMenu from "../SideMenu/SideMenu"; import MenuItems from "../../../data/MenuItems/MenuItems"; const NavBar = () => { const [menuIsOpen, setMenuIsOpen] = useState(false); const toggleMenu = () => { if (menuIsOpen === false) { setMenuIsOpen(true); } else { setMenuIsOpen(false); } }; return ( <nav className={styles.navBarBox}> <div className={styles.menuIcon}> <i className={"fas fa-bars fa-2x"} onClick={toggleMenu}></i> </div> <div className={styles.mobileSideMenu}> {menuIsOpen ? <SideMenu /> : <div></div>} </div> <ul className={styles.navMenu}> {MenuItems.map((item) => ( <li className={styles.navItem} key={item.id}> <Link href={item.path}>{item.title}</Link> </li> ))} </ul> <Link href="/contact-me"> <a className={styles.contactMeWeb}> <ButtonBase label="Contact Me" isPrimary={false}></ButtonBase> </a> </Link> <Link href="/contact-me"> <a className={styles.contactMeMobile}> <i className={"fa-solid fa-message fa-2x"}> </i> </a> </Link> </nav> ); };
接入OutsideClickHandler的NavBar.js(外部点击关闭生效,但侧边菜单链接被判定为外部点击失效)
import { useState } from "react"; import Link from "next/link"; import OutsideClickHandler from "react-outside-click-handler"; import styles from "../NavBar/NavBar.module.css"; import ButtonBase from "../button/buttonPrimary"; import SideMenu from "../SideMenu/SideMenu"; import MenuItems from "../../../data/MenuItems/MenuItems"; const NavBar = () => { const [menuIsOpen, setMenuIsOpen] = useState(false); return ( <nav className={styles.navBarBox}> <OutsideClickHandler onOutsideClick={() => { setMenuIsOpen(false); }} > {/* BURGER ICON */} <a className={styles.menuIcon} onClick={() => setMenuIsOpen(!menuIsOpen)} > <i className={"fas fa-bars fa-2x"}></i> </a> </OutsideClickHandler> {/* SIDE MENU */} {menuIsOpen && ( <div className={styles.mobileSideMenu}> <SideMenu /> </div> )} <ul className={styles.navMenu}> {MenuItems.map((item) => ( <li className={styles.navItem} key={item.id}> <Link href={item.path}>{item.title}</Link> </li> ))} </ul> <Link href="/contact-me"> <a className={styles.contactMeWeb}> <ButtonBase label="Contact Me" isPrimary={false}></ButtonBase> </a> </Link> <Link href="/contact-me"> <a className={styles.contactMeMobile}> <i className="fa-regular fa-envelope fa-2x"></i> </a> </Link> </nav> ); }; export default NavBar;
SideMenu.js组件代码
import MenuItems from "../../../data/MenuItems/MenuItems"; import styles from "../SideMenu/SideMenu.module.css"; const SideMenu = () => { return ( <div className={styles.mobileMenuBox}> <ul className={styles.mobileMenu}> {MenuItems.map((item) => ( <li className={styles.mobileItem} key={item.id}> <Link href={item.path}><div>{item.title}</div></Link> </li> ))} </ul> </div> ); }; export default SideMenu;
问题根因
你的推测完全正确:react-outside-click-handler的判定逻辑是点击事件触发的目标元素不在组件包裹的子DOM树范围内时,触发onOutsideClick回调。当前代码里你只把汉堡按钮放在了OutsideClickHandler的包裹范围内,展开的侧边菜单渲染在包裹范围外,所以点击侧边菜单时会直接触发关闭逻辑,菜单组件被立即卸载,内部链接的跳转逻辑还没执行就被打断,最终表现为链接点击失效。
修复方案
不需要把状态逻辑下沉到SideMenu组件,只需要调整OutsideClickHandler的包裹范围,把汉堡按钮和侧边菜单都纳入包裹范围即可,菜单开关状态依然由NavBar组件统一维护。
修复后的NavBar.js核心代码如下:
import { useState } from "react"; import Link from "next/link"; import OutsideClickHandler from "react-outside-click-handler"; import styles from "../NavBar/NavBar.module.css"; import ButtonBase from "../button/buttonPrimary"; import SideMenu from "../SideMenu/SideMenu"; import MenuItems from "../../../data/MenuItems/MenuItems"; const NavBar = () => { const [menuIsOpen, setMenuIsOpen] = useState(false); return ( <nav className={styles.navBarBox}> {/* 汉堡按钮+侧边菜单全部放在OutsideClickHandler包裹范围内 */} <OutsideClickHandler onOutsideClick={() => { setMenuIsOpen(false); }} > {/* 汉堡图标 */} <a className={styles.menuIcon} onClick={() => setMenuIsOpen(!menuIsOpen)} > <i className={"fas fa-bars fa-2x"}></i> </a> {/* 侧边菜单移到包裹范围内 */} {menuIsOpen && ( <div className={styles.mobileSideMenu}> <SideMenu /> </div> )} </OutsideClickHandler> <ul className={styles.navMenu}> {MenuItems.map((item) => ( <li className={styles.navItem} key={item.id}> <Link href={item.path}>{item.title}</Link> </li> ))} </ul> <Link href="/contact-me"> <a className={styles.contactMeWeb}> <ButtonBase label="Contact Me" isPrimary={false}></ButtonBase> </a> </Link> <Link href="/contact-me"> <a className={styles.contactMeMobile}> <i className="fa-regular fa-envelope fa-2x"></i> </a> </Link> </nav> ); }; export default NavBar;
额外注意事项
你当前的SideMenu.js组件中使用了<Link>标签但没有引入NextJS的Link组件,运行时会报未定义错误,记得在SideMenu.js顶部补上导入语句:
import Link from "next/link";
调整后点击侧边菜单区域不会触发外部点击关闭逻辑,点击菜单内链接可以正常完成路由跳转,点击页面其他区域依然会自动关闭菜单,符合预期交互。
内容的提问来源于stack exchange,提问作者Claireyskillz
相关产品推荐
相关产品推荐

