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

React中使用useState切换overflow样式实现滚动条锁定解锁

问题根源

你当前代码的逻辑缺陷非常明确:toggleClass 函数中没有对菜单状态做分支判断,无论点击后是展开还是收起菜单,都会强制把 document.body.style.overflow 设为 hidden,所以关闭菜单时滚动锁定永远不会被解除。
另外不推荐在点击事件回调里直接混写DOM操作:React的状态更新是异步的,直接读取当前isActive值做判断很容易拿到旧值,导致状态和UI表现不一致。

正确实现方案

最稳妥的写法是把滚动锁定的逻辑作为isActive状态的副作用,用useEffect监听状态变化自动同步,保证DOM属性和组件状态100%匹配:

import { useState, useEffect } from 'react';

const [isActive, setActive] = useState(false);

// 菜单状态变化时自动同步滚动锁定规则
useEffect(() => {
  // 展开时锁滚动,收起时恢复
  document.body.style.overflow = isActive ? 'hidden' : 'unset';
  // 组件卸载时兜底恢复滚动,避免组件销毁后页面被意外锁定
  return () => {
    document.body.style.overflow = 'unset';
  };
}, [isActive]);

const toggleMenu = () => {
  // 点击事件只负责切换状态,DOM修改统一交给副作用处理
  setActive(prevState => !prevState);
};

return (
  <div
    onClick={toggleMenu}
    className={isActive ? 'nav-button closebtn active' : 'nav-button closebtn'}
  >
    <span></span>
  </div>
);

可选简化写法(不推荐)

如果你不想引入useEffect,也可以在setActive的状态更新回调里拿到最新状态做判断,但这种写法无法覆盖组件初始化、卸载时的状态兜底,容易出边界问题:

const toggleClass = () => {
  setActive(prev => {
    const nextActive = !prev;
    document.body.style.overflow = nextActive ? 'hidden' : 'unset';
    return nextActive;
  })
};

补充说明:如果你的页面本身给body设置过自定义overflow值,不要直接写死unset,可以在锁定前先存下原始的overflow值,解锁时恢复成原始值即可,避免覆盖原有页面样式。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 19:39:12