使用react-intersection-observer懒加载时如何避免组件频繁重挂载?
解决React-intersection-observer懒加载组件滚出视口卸载后重挂载导致的滚动跳转问题
问题分析
当前实现的核心问题在于:LazyComponent仅在目标组件处于视口内(inView为true)时渲染它,一旦组件滚出视口,inView变为false,目标组件就会被卸载。当再次滚动回视口时,组件重新挂载,由于目标组件高度较大,浏览器会自动调整滚动位置到新挂载元素的顶部,引发页面跳转。
解决方案
核心思路是让目标组件在首次加载后保持挂载状态,不再因滚出视口而卸载。通过添加状态变量记录组件是否已加载,结合inView触发首次加载逻辑即可解决问题。
修改后的完整代码
import React, { useState, useEffect } from "react"; import { useInView } from "react-intersection-observer"; const LazyComponent = ({ LoadableComponent, ...rest }) => { const { ref, inView } = useInView({ rootMargin: "50px 0px" }); // 记录组件是否已完成首次加载 const [isLoaded, setIsLoaded] = useState(false); // 当组件首次进入视口时,标记为已加载(仅触发一次) useEffect(() => { if (inView) { setIsLoaded(true); } }, [inView]); // 一旦加载完成,始终保持组件挂载状态 return ( <div ref={ref}> {isLoaded && <LoadableComponent {...rest} />} </div> ); }; const MyComponent = (props) => { return ( <div style={{ height: "4000px", backgroundColor: "blue" }}> <p>This is the top of MyComponent.</p> </div> ); }; export default function App() { return ( <div className="App"> <h1>Title</h1> <div style={{ height: "1000px", backgroundColor: "green" }}></div> <LazyComponent LoadableComponent={MyComponent} /> <div style={{ height: "1000px", backgroundColor: "red" }}></div> </div> ); }
关键逻辑说明
- 新增
isLoaded状态变量,专门用于记录目标组件是否已完成首次加载。 - 通过
useEffect监听inView的变化,仅在组件首次进入视口时将isLoaded设为true,确保加载逻辑只触发一次。 - 渲染逻辑改为仅依赖
isLoaded,一旦组件加载完成,无论是否在视口内,都保持挂载状态,彻底避免了卸载后重挂载导致的滚动跳转。
可选优化方案(隐藏而非卸载)
如果需要在组件滚出视口时隐藏但保留DOM结构(避免重复初始化开销),可以调整渲染逻辑为:
return ( <div ref={ref} style={{ visibility: isLoaded ? "visible" : "hidden", height: isLoaded ? "auto" : "0", overflow: "hidden" }} > <LoadableComponent {...rest} /> </div> );
这种方式下,组件始终存在于DOM中,仅通过CSS控制可见性和高度,同样可以避免滚动跳转,适合不需要完全卸载的场景。
内容的提问来源于stack exchange,提问作者Ruben Lemiengre
相关产品推荐
相关产品推荐

