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

React中useEffect内useState获取旧值问题求助

问题原因

你的问题出在闭包陷阱上:useEffect的依赖数组是空的,这意味着onScrollIntentTriggerFn只在组件首次渲染时创建一次,它捕获的isScrollReached始终是初始值false。哪怕之后通过setIsScrollReached(true)更新了状态,这个函数里的isScrollReached依然是旧值,所以滚动回顶部时的判断条件永远不成立。

解决方案

下面提供几种可行的修复方式:

方法1:将状态加入useEffect依赖数组

把isScrollReached添加到useEffect的依赖列表中,这样每次状态变化时,都会重新创建onScrollIntentTriggerFn并绑定新的滚动事件,确保函数能拿到最新的状态值。

const [isScrollReached, setIsScrollReached] = useState(false);

const onScrollIntentTriggerFn = event => {
  const scrollRatio = window.pageYOffset / document.body.offsetHeight;
  if (scrollRatio > 0.6) {
    setIsScrollReached(true);
  }
  if (scrollRatio < 0.05 && isScrollReached) {
    console.log('User scroll to 60% and came back to top of the screen');
  }
};

useEffect(() => {
  window.addEventListener('scroll', onScrollIntentTriggerFn);
  return () => {
    window.removeEventListener('scroll', onScrollIntentTriggerFn);
  };
}, [isScrollReached]); // 添加状态到依赖数组

方法2:使用useRef保存最新状态

利用useRef的current属性可变且不受闭包影响的特性,实时同步状态值,在滚动函数里直接访问ref.current获取最新状态。

const [isScrollReached, setIsScrollReached] = useState(false);
const scrollReachedRef = useRef(false);

// 同步状态到ref
useEffect(() => {
  scrollReachedRef.current = isScrollReached;
}, [isScrollReached]);

const onScrollIntentTriggerFn = event => {
  const scrollRatio = window.pageYOffset / document.body.offsetHeight;
  if (scrollRatio > 0.6) {
    setIsScrollReached(true);
  }
  // 访问ref.current获取最新状态
  if (scrollRatio < 0.05 && scrollReachedRef.current) {
    console.log('User scroll to 60% and came back to top of the screen');
  }
};

useEffect(() => {
  window.addEventListener('scroll', onScrollIntentTriggerFn);
  return () => {
    window.removeEventListener('scroll', onScrollIntentTriggerFn);
  };
}, []); // 依赖数组保持为空

方法3:使用useCallback包裹滚动函数

结合useCallback缓存函数,当依赖变化时重新生成函数,确保函数能捕获最新的状态。

const [isScrollReached, setIsScrollReached] = useState(false);

// 使用useCallback包裹,依赖isScrollReached
const onScrollIntentTriggerFn = useCallback(event => {
  const scrollRatio = window.pageYOffset / document.body.offsetHeight;
  if (scrollRatio > 0.6) {
    setIsScrollReached(true);
  }
  if (scrollRatio < 0.05 && isScrollReached) {
    console.log('User scroll to 60% and came back to top of the screen');
  }
}, [isScrollReached]);

useEffect(() => {
  window.addEventListener('scroll', onScrollIntentTriggerFn);
  return () => {
    window.removeEventListener('scroll', onScrollIntentTriggerFn);
  };
}, [onScrollIntentTriggerFn]); // 依赖useCallback返回的函数
补充说明
  • 方法1和方法3逻辑类似,都是通过更新函数引用,让滚动事件监听最新状态;方法2则是绕过闭包,直接访问可变的ref值。
  • 推荐优先使用方法1或方法2,逻辑更直观易维护。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 16:23:16