如何阻止scrollPosition变化后useEffect重复触发渲染?
解决滚动触发页面重复渲染及首次滚动后停止更新scrollPosition的问题
核心问题分析
原代码存在三个关键问题:
useEffect依赖scrollPosition,导致每次滚动更新scrollPosition后都会重新执行useEffect,反复添加/移除滚动监听,形成循环触发handleScroll函数每次组件渲染都会重新创建,导致removeEventListener无法正确移除之前添加的监听(函数引用不一致)- 未实现“首次滚动后停止更新scrollPosition”的目标,反而因频繁触发状态更新导致页面刷新、动画组件重置
解决方案
方案1:首次滚动后立即停止跟踪(最简实现)
通过useCallback保持handleScroll引用稳定,首次触发滚动后直接移除监听,后续滚动不再更新scrollPosition:
import { useState, useEffect, useCallback } from 'react'; function App() { const [scrollPosition, setScrollPosition] = useState(0); // 用useCallback固定函数引用,确保移除监听时匹配正确 const handleScroll = useCallback(() => { const position = window.scrollY; setScrollPosition(position); // 首次滚动后立即移除监听,停止后续更新 window.removeEventListener('scroll', handleScroll); }, []); useEffect(() => { // 组件挂载时仅添加一次监听 window.addEventListener('scroll', handleScroll, { passive: true }); // 组件卸载时清理监听,避免内存泄漏 return () => { window.removeEventListener('scroll', handleScroll); }; }, [handleScroll]); return ( <div>当前滚动位置:{scrollPosition}</div> ); }
方案2:通过状态控制监听开关(支持重新开启)
如果需要后续重新启用滚动跟踪,可以通过animating状态控制,首次滚动后关闭监听:
import { useState, useEffect, useCallback } from 'react'; function App() { const [scrollPosition, setScrollPosition] = useState(0); const [animating, setAnimating] = useState(true); // 初始允许监听 const handleScroll = useCallback(() => { const position = window.scrollY; setScrollPosition(position); // 首次滚动后关闭监听 setAnimating(false); }, []); useEffect(() => { if (animating) { window.addEventListener('scroll', handleScroll, { passive: true }); } else { window.removeEventListener('scroll', handleScroll); } return () => { window.removeEventListener('scroll', handleScroll); }; }, [animating, handleScroll]); // 可选:提供重新开启跟踪的方法 const restartTracking = () => setAnimating(true); return ( <div> 当前滚动位置:{scrollPosition} <button onClick={restartTracking}>重新跟踪滚动</button> </div> ); }
关键优化点
- 稳定函数引用:用
useCallback包裹handleScroll,确保removeEventListener能精准移除对应监听 - 调整依赖项:将
useEffect的依赖改为稳定的handleScroll或animating,避免因scrollPosition变化导致的循环触发 - 终止更新循环:通过“触发后移除监听”或“状态切换关闭监听”的逻辑,从根源上阻止
scrollPosition反复更新引发的渲染
内容的提问来源于stack exchange,提问作者schnondle
相关产品推荐
相关产品推荐

