React/JavaScript中窗口resize时如何保持滚动位置并避免页面跳转
解决递归渲染树形帖子时滚动加载的页面跳转问题
核心问题分析
你遇到的页面跳转,本质是新内容递归渲染时DOM高度多次变化,导致scrollTo执行时机不对,加上依赖height作为触发条件,会在DOM未完全稳定时就执行滚动,自然没效果。
具体解决方案
1. 提前记录滚动位置,避免依赖不稳定的height
不要等height变化后再拿位置,而是在触发加载新内容的瞬间就记录当前滚动位置:
// 定义状态存储滚动位置 const [prevScrollPos, setPrevScrollPos] = useState(0); // 滚动触发加载的逻辑 const handleScroll = () => { const isBottom = window.innerHeight + window.scrollY >= document.body.offsetHeight - 50; if (isBottom) { // 加载前立即记录当前滚动位置 setPrevScrollPos(window.scrollY); // 执行加载新帖子的逻辑 loadMorePosts(); } }; // 挂载时绑定滚动监听 useEffect(() => { window.addEventListener('scroll', handleScroll); return () => window.removeEventListener('scroll', handleScroll); }, []);
2. 确保DOM完全渲染后再恢复滚动位置
递归组件的渲染是异步的,直接在useEffect里执行scrollTo会早于DOM更新。用requestAnimationFrame延迟执行,保证DOM稳定:
useEffect(() => { // 等DOM更新完成后再滚动 requestAnimationFrame(() => { window.scrollTo({ top: prevScrollPos, behavior: 'instant' // 禁用平滑滚动,避免位置偏差 }); }); }, [posts]); // 依赖posts数组(加载后的新内容),而非height
3. 给递归组件加唯一key,减少不必要重排
递归渲染时,每个帖子节点必须有唯一的key,让React正确识别DOM结构,避免不必要的重绘重排导致滚动异常:
const PostItem = ({ item }) => { return ( <div key={`post-${item.id}-${item.depth}`}> {/* 用id+层级确保唯一 */} <div className="post-content">{item.message}</div> {/* 递归渲染子帖子 */} {item.sub?.map(child => ( <PostItem item={{ ...child, depth: (item.depth || 0) + 1 }} /> ))} </div> ); };
4. 可选:固定滚动容器,避免全局body高度波动
如果用全局body滚动容易出问题,可以把帖子列表放在固定高度的滚动容器内,只监听容器的滚动事件,这样滚动位置的控制更精准:
// 滚动容器的ref const scrollContainerRef = useRef(null); const handleContainerScroll = () => { const container = scrollContainerRef.current; const isBottom = container.scrollTop + container.clientHeight >= container.scrollHeight - 50; if (isBottom) { setPrevScrollPos(container.scrollTop); loadMorePosts(); } }; // 恢复滚动位置时操作容器 useEffect(() => { requestAnimationFrame(() => { scrollContainerRef.current.scrollTop = prevScrollPos; }); }, [posts]); // 渲染时用容器包裹 return ( <div ref={scrollContainerRef} style={{ height: '100vh', overflowY: 'auto' }}> {posts.map(post => <PostItem item={post} />)} </div> );
内容的提问来源于stack exchange,提问作者Alex Hofer
相关产品推荐
相关产品推荐

