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

如何在useCallback钩子中移除滚动事件监听器?

解决React滚动钩子的事件监听器移除问题

你的核心问题是用useCallback实现ref回调时,未处理旧元素的事件监听器移除,可能引发内存泄漏。以下是修正后的实现方案:

修正后的钩子代码

import { useState, useRef, useCallback } from 'react';

function useHasScrolled() {
  const [hasScrolled, setHasScrolled] = useState(false);
  const elementRef = useRef(null);

  // 稳定滚动处理函数的引用
  const handleScroll = useCallback((e) => {
    setHasScrolled(e.target.scrollTop !== 0);
  }, []);

  const setRef = useCallback((element) => {
    // 移除旧元素的滚动监听
    if (elementRef.current) {
      elementRef.current.removeEventListener('scroll', handleScroll);
    }

    // 给新元素绑定滚动监听
    if (element) {
      element.addEventListener('scroll', handleScroll);
    }

    // 更新当前元素的引用
    elementRef.current = element;
  }, [handleScroll]);

  return {
    hasScrolled,
    scrollingElementRef: setRef
  };
}

关键改进点

  • 稳定处理函数引用:用useCallback包裹handleScroll,确保添加和移除监听时使用同一个函数实例,否则removeEventListener无法生效。
  • 清理旧元素监听:绑定新元素前,先检查elementRef.current中保存的旧元素,移除它的滚动事件监听器,避免内存泄漏。
  • 自动处理卸载场景:组件卸载时,React会自动调用ref回调并传入null,此时代码会移除最后一个绑定元素的监听,完成清理。

使用方式(保持不变)

const { hasScrolled, scrollingElementRef } = useHasScrolled();

return (
  <div ref={scrollingElementRef} style={{ overflow: 'auto', height: '300px' }}>
    {/* 滚动内容 */}
  </div>
);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 02:35:24