如何将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
相关产品推荐
相关产品推荐

