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

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;
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;
}

关键修复说明

  1. 事件监听器优化:使用useRef获取导航栏元素,通过useEffect只绑定一次drag事件,并在组件卸载时移除监听器,避免重复绑定导致的状态异常。
  2. 修复组件返回值:NavBar组件添加外层nav元素并通过条件渲染显示关闭按钮,确保按钮能正常渲染。
  3. 修正OpenNavBtn结构:将return语句移到组件顶层,确保组件能正确返回按钮元素,状态切换逻辑正常执行。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 13:10:23