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

自定义useScrollPosition Hook检测window.pageYOffset异常问题求助

问题分析

当滚动位置在100px临界值附近时,微小的滚动动作都会让window.pageYOffset > 100的结果反复切换,再加上scroll事件本身触发频率极高,直接导致状态频繁更新,进而引发UI闪烁。另外原Hook未清理滚动监听,存在内存泄漏风险。

解决方案

方案1:添加滞后阈值(推荐)

设置两个不同的触发阈值,给状态切换留一个缓冲区间,彻底避免临界值附近的频繁切换:

const useScrollPosition = () => {
  const [scrollPosition, setScrollPosition] = useState(false);
  // 定义上下阈值:向下滚动超过120才隐藏,向上滚动低于80才显示
  const SCROLL_DOWN_THRESHOLD = 120;
  const SCROLL_UP_THRESHOLD = 80;

  useEffect(() => {
    if (typeof window === 'undefined') return;

    const handleScroll = () => {
      const currentY = window.pageYOffset;
      setScrollPosition(prev => {
        // 向下滚动:当前位置超过下阈值,且之前未隐藏,才更新状态
        if (currentY > SCROLL_DOWN_THRESHOLD && !prev) {
          return true;
        }
        // 向上滚动:当前位置低于上阈值,且之前已隐藏,才更新状态
        if (currentY < SCROLL_UP_THRESHOLD && prev) {
          return false;
        }
        // 其他情况保持原状态,不触发组件更新
        return prev;
      });
    };

    window.addEventListener('scroll', handleScroll);
    // 组件卸载时清理监听
    return () => window.removeEventListener('scroll', handleScroll);
  }, []);

  return [scrollPosition];
};

export default useScrollPosition;

方案2:防抖处理

通过限制状态更新的频率,避免短时间内多次触发状态变更:

const useScrollPosition = () => {
  const [scrollPosition, setScrollPosition] = useState(false);

  useEffect(() => {
    if (typeof window === 'undefined') return;

    let timeoutId;
    const handleScroll = () => {
      // 清除之前的定时器,确保只在滚动停止后一段时间才更新状态
      clearTimeout(timeoutId);
      timeoutId = setTimeout(() => {
        setScrollPosition(window.pageYOffset > 100);
      }, 100); // 100ms防抖时间,可根据需求调整
    };

    window.addEventListener('scroll', handleScroll);
    return () => {
      window.removeEventListener('scroll', handleScroll);
      clearTimeout(timeoutId); // 清理残留定时器
    };
  }, []);

  return [scrollPosition];
};

export default useScrollPosition;
额外优化建议
  • 可以将Hook返回值改为对象,提高可读性:return { isScrolled: scrollPosition };,使用时直接解构:const { isScrolled } = useScrollPosition();
  • 若使用React 18+,可结合useEffectEvent抽离滚动处理函数,进一步优化依赖逻辑,但上述方案已能满足稳定需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 00:54:28