React中scroll事件监听器传递函数引用不触发,包裹箭头函数却正常运行的原因排查
为什么直接传函数引用给scroll监听器无效,箭头函数包裹却能正常工作?
这种情况确实挺让人困惑的,我来帮你拆解可能的原因和解决思路:
1. 你的清理函数写法有误(最可能的核心问题)
你当前的useEffect返回的是立即执行的移除操作,而不是一个待触发的清理函数:
// 错误写法:刚添加监听器就立刻移除了! return window.removeEventListener("scroll", handleAutoplay);
这会导致useEffect执行时,监听器刚挂载就被移除,自然不会触发任何事件。
而当你用箭头函数包裹时,每次渲染都会生成一个新的箭头函数,此时removeEventListener移除的是上一次渲染的箭头函数,当前的新箭头函数还挂在window上,所以能触发——但这会造成内存泄漏,旧的监听器永远无法被清理。
正确的写法应该是返回一个函数,让React在组件卸载或依赖变化时再执行移除操作:
useEffect(() => { console.log("glo"); window.addEventListener("scroll", handleAutoplay); // 正确:返回清理函数,React会在合适时机调用它 return () => window.removeEventListener("scroll", handleAutoplay); },[handleAutoplay])
2. 页面没有可滚动空间
如果你的页面内容高度小于浏览器视口高度,浏览器根本不会触发scroll事件。你可以临时给页面加个高容器测试:
<div style={{height: '200vh', border: '1px solid #ccc'}}>滚动这个区域试试</div>
先排除这个基础条件是否满足。
3. React严格模式的干扰(仅开发环境)
在React开发环境的严格模式下,useEffect会被执行两次,这可能导致监听器被重复添加/移除,但只要清理函数写法正确,这只会在开发环境出现,生产环境不会有问题。不过如果清理函数写错,就会放大问题。
修复后的完整代码
import { useCallback, useEffect } from 'react'; function YourComponent() { const handleAutoplay = useCallback(() => { console.log("test"); }, []); useEffect(() => { console.log("glo"); window.addEventListener("scroll", handleAutoplay); // 返回正确的清理函数 return () => window.removeEventListener("scroll", handleAutoplay); },[handleAutoplay]) return ( <div style={{height: '200vh'}}> 滚动页面看控制台输出 </div> ); } export default YourComponent;
这样修改后,直接传递函数引用应该就能正常触发scroll事件了。
内容的提问来源于stack exchange,提问作者Zenek Wiaderko
相关产品推荐
相关产品推荐

