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

React中useState在useEffect/事件监听内无法更新值的问题

React滚动监听中useState状态不更新的问题解决

问题原因

  1. 闭包陷阱:你的useEffect依赖数组是空的,回调只会在组件挂载时执行一次。此时事件监听器捕获的scrollSection是初始值0,后续即使状态更新,监听器里拿到的还是这个旧值,所以你打印的scrollSection永远是0,看起来像状态没更新。
  2. 监听移除失效:你在addEventListener和removeEventListener里用了不同的箭头函数(每次都是新创建的),导致组件卸载时无法正确移除滚动监听,会造成内存泄漏。
  3. 状态更新的即时性:setScrollSection是异步的,而且你打印的是传入的旧scrollSection参数,即使状态更新了,这里也看不到最新值。

修复方案

方案1:使用函数式更新+稳定的监听回调

把事件回调定义在useEffect内部,用函数式更新获取最新状态,避免依赖scrollSection:

function SideNav(props: any) {
  const [scrollSection, setScrollSection] = React.useState(0);

  React.useEffect(() => {
    const handleScroll = () => {
      const scrollY = window.scrollY;
      // 函数式更新,拿到最新的状态值
      setScrollSection(prev => {
        // 这里替换成你的实际判断逻辑,比如根据scrollY切换section
        if (scrollY > prev) {
          return 1;
        }
        return prev;
      });
      console.log("scroll Y: "+scrollY);
    };

    window.addEventListener('scroll', handleScroll);
    // 移除监听时用同一个handleScroll函数
    return () => window.removeEventListener('scroll', handleScroll);
  }, []);
}

方案2:复用函数+正确设置依赖

如果checkSection需要复用,可以用useCallback包裹,同时正确设置依赖:

function SideNav(props: any) {
  const [scrollSection, setScrollSection] = React.useState(0);

  const checkSection = React.useCallback(() => {
    const scrollSections = [0, 1538, 2583, 2089, 3089];
    const scrollY = window.scrollY;
    setScrollSection(1);
    console.log("scroll Y: "+scrollY);
  }, []);

  React.useEffect(() => {
    window.addEventListener('scroll', checkSection);
    return () => window.removeEventListener('scroll', checkSection);
  }, [checkSection]);

  // 单独监听scrollSection变化,打印最新值
  React.useEffect(() => {
    console.log("最新section: "+scrollSection);
  }, [scrollSection]);
}

额外优化建议

滚动监听触发频率很高,建议加节流处理避免性能问题:

// 自己实现简单节流函数
function throttle(func: Function, delay: number) {
  let lastCall = 0;
  return (...args: any[]) => {
    const now = Date.now();
    if (now - lastCall >= delay) {
      func(...args);
      lastCall = now;
    }
  };
}

// 在useEffect里使用
const handleScroll = React.useCallback(throttle(() => {
  // 你的滚动逻辑
}, 100), []);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 17:25:26