React组件传状态与setter函数异常:菜单开关状态失效
问题分析与修复方案
核心问题点
- 重复绑定事件监听器:
NavBar组件的closeNav函数中,每次触发onMouseDown都会给.gaming__navbar元素添加新的drag事件监听器,多次点击后会积累多个监听器,导致状态被多次反转,最终出现点击关闭按钮无反应的情况。 - 组件未返回JSX:
NavBar组件定义了displayCloseBtn但没有将其作为返回值返回,导致关闭按钮根本没有渲染到页面上。 - OpenNavBtn组件结构错误:
return语句被写在了openNav函数内部,组件本身没有返回任何JSX元素,导致打开按钮无法正常渲染。
修复后的代码
App.js
import "./App.css"; import { Fragment, useState } from "react"; import HeroSection from "./HeroSection"; import NavBar from "./NavBar"; function App() { const [mainMenuIsClosed, setMainMenuIsClosed] = useState(true); return ( <Fragment> <NavBar mainMenuIsClosed={mainMenuIsClosed} setMainMenuIsClosed={setMainMenuIsClosed} /> <HeroSection mainMenuIsClosed={mainMenuIsClosed} setMainMenuIsClosed={setMainMenuIsClosed} /> </Fragment> ); } export default App;
NavBar.js
import "./NavBar.css"; import { useEffect, useRef } from "react"; export default function NavBar(props) { const navbarRef = useRef(null); useEffect(() => { const handleDrag = () => { navbarRef.current.classList.remove("gaming__navbar-opened"); props.setMainMenuIsClosed(prev => !prev); }; if (navbarRef.current) { navbarRef.current.addEventListener("drag", handleDrag); } // 组件卸载时移除监听器,避免内存泄漏 return () => { if (navbarRef.current) { navbarRef.current.removeEventListener("drag", handleDrag); } }; }, [props.setMainMenuIsClosed]); const closeNav = () => { navbarRef.current.classList.remove("gaming__navbar-opened"); props.setMainMenuIsClosed(prev => !prev); }; return ( <nav ref={navbarRef} className={`gaming__navbar ${!props.mainMenuIsClosed ? 'gaming__navbar-opened' : ''}`}> {!props.mainMenuIsClosed && ( <button onMouseDown={closeNav} className="gaming__navbar-close-btn"> <img className="gaming__navbar-close-icon" src="../Assets/arrowBack.png" alt="关闭导航" /> </button> )} {/* 可添加导航栏其他内容 */} </nav> ); }
HeroSection.js
import "./HeroSection.css"; import OpenNavBtn from "./OpenNavBtn"; export default function HeroSection(props) { return ( <section className="gaming__hero-section"> <OpenNavBtn mainMenuIsClosed={props.mainMenuIsClosed} setMainMenuIsClosed={props.setMainMenuIsClosed} /> {/* 英雄区其他内容 */} </section> ); }
OpenNavBtn.js
import "./OpenNavBtn.css"; import MenuSvg from "./MenuSvg"; // 确保该组件正确导入 export default function OpenNavBtn({ mainMenuIsClosed, setMainMenuIsClosed }) { const openNav = () => { setMainMenuIsClosed(prev => !prev); }; return mainMenuIsClosed ? ( <button onClick={openNav} className="gaming__open-nav-btn"> <MenuSvg /> </button> ) : null; }
关键修复说明
- 事件监听器优化:使用
useRef获取导航栏元素,通过useEffect只绑定一次drag事件,并在组件卸载时移除监听器,避免重复绑定导致的状态异常。 - 修复组件返回值:
NavBar组件添加外层nav元素并通过条件渲染显示关闭按钮,确保按钮能正常渲染。 - 修正OpenNavBtn结构:将
return语句移到组件顶层,确保组件能正确返回按钮元素,状态切换逻辑正常执行。
内容的提问来源于stack exchange,提问作者Zglob
相关产品推荐
相关产品推荐

