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

如何将document的scroll监听逻辑抽离到独立文件供React组件复用

问题排查

你的实现存在以下几个核心问题:

  • 全局事件绑定错误:你在function.ts的顶层作用域直接执行了document.addEventListener("scroll", trackScrolling),模块加载时就会触发绑定,此时调用trackScrolling没有传入element和callback参数,直接抛出参数未定义的运行时错误。
  • 滚动监听逻辑缺失:你当前的trackScrolling函数仅在调用时执行一次判断,没有把判断逻辑封装为滚动事件的回调函数,滚动时不会重复触发触底检测。
  • 事件移除逻辑失效:你移除监听时传入的是外层导出的trackScrolling函数,和实际绑定到scroll事件的回调不是同一个引用,会导致监听无法正常移除,引发内存泄漏。
  • 组件侧缺少副作用清理:你在useEffect中调用逻辑没有返回清理函数,组件卸载时没有移除对应的滚动监听,也会引发内存泄漏。
修复后的代码

function.ts 工具函数文件

const checkBottomDiv = (el: HTMLElement, offset: number = 400) => {
  return el.getBoundingClientRect().bottom - offset <= window.innerHeight;
};

export const trackScrolling = (element: HTMLElement, callback: () => void, offset?: number) => {
  // 每个调用生成独立的监听实例,支持多组件同时调用不冲突
  const scrollHandler = () => {
    if (checkBottomDiv(element, offset)) {
      callback();
      // 触发后自动移除当前监听
      document.removeEventListener("scroll", scrollHandler);
    }
  };
  document.addEventListener("scroll", scrollHandler);
  // 返回清理函数供外部调用
  return () => {
    document.removeEventListener("scroll", scrollHandler);
  };
};

app.tsx 组件文件

import { useEffect } from 'react';
import { trackScrolling } from './function';

// 你的业务回调
const callback = () => {
  console.log("触达底部阈值");
};

const App = () => {
  useEffect(() => {
    const element = document.getElementById("myDiv") as HTMLElement;
    // 元素不存在直接跳过
    if (!element) return;
    const removeListener = trackScrolling(element, callback, 400);
    // 组件卸载时清理监听
    return () => {
      removeListener();
    };
  }, []);

  return (
    <div id="myDiv" style={{ height: '200vh' }}>
      测试滚动容器
    </div>
  );
};

export default App;
可选优化点
  • 可以将滚动绑定对象作为参数传入,支持容器内部滚动监听而非仅全局滚动监听
  • 可增加防抖逻辑,减少滚动时的判断执行频率,提升性能

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 18:45:03