React 无限滚动的完整指南

无限滚动是一种用户体验模式,当用户向下滚动页面时加载新内容。这种模式常见于社交媒体信息流、时间线和新闻应用中,它是一种流畅浏览大型数据集的方式,无需使用分页按钮。在本指南中,你将学习如何使用第三方库和自定义钩子在 React 中实现无限滚动。
要点
- 学习在 React 中实现无限滚动的 2 种实用方法
- 使用带有 IntersectionObserver 的自定义钩子或第三方包
- 处理加载状态、分页和空结果
什么是无限滚动?
无限滚动在用户滚动到列表末尾附近时加载新数据。它消除了分页的需求,创造了连续的浏览体验。你可以在 Instagram、Twitter 和 Reddit 等产品中看到这种模式。
方法一:使用第三方包(react-infinite-scroll-component
)
在 React 中实现无限滚动的最简单方法是使用经过充分测试的包。
第一步:安装
npm install react-infinite-scroll-component
第二步:基本使用示例
import InfiniteScroll from 'react-infinite-scroll-component';
function Feed() {
const [items, setItems] = useState([]);
const [page, setPage] = useState(1);
const [hasMore, setHasMore] = useState(true);
const fetchMoreData = async () => {
const res = await fetch(`/api/posts?page=${page}`);
const newItems = await res.json();
if (newItems.length === 0) setHasMore(false);
else {
setItems(prev => [...prev, ...newItems]);
setPage(prev => prev + 1);
}
};
return (
<InfiniteScroll
dataLength={items.length}
next={fetchMoreData}
hasMore={hasMore}
loader={<h4>Loading...</h4>}
endMessage={<p>No more results</p>}
>
{items.map(item => <div key={item.id}>{item.title}</div>)}
</InfiniteScroll>
);
}
优点
- 快速设置
- 内置加载和结束状态
缺点
- 对滚动逻辑的控制较少
- 增加了包依赖
方法二:使用 IntersectionObserver
的自定义钩子
这种方法给你完全的控制权,且无额外依赖。
第一步:创建钩子
function useInfiniteScroll(callback, ref) {
useEffect(() => {
const observer = new IntersectionObserver(([entry]) => {
if (entry.isIntersecting) callback();
});
if (ref.current) observer.observe(ref.current);
return () => observer.disconnect();
}, [callback, ref]);
}
第二步:在组件中使用
function Feed() {
const [items, setItems] = useState([]);
const [page, setPage] = useState(1);
const [hasMore, setHasMore] = useState(true);
const sentinelRef = useRef(null);
const loadMore = async () => {
if (!hasMore) return;
const res = await fetch(`/api/posts?page=${page}`);
const newItems = await res.json();
if (newItems.length === 0) setHasMore(false);
else {
setItems(prev => [...prev, ...newItems]);
setPage(prev => prev + 1);
}
};
useInfiniteScroll(loadMore, sentinelRef);
return (
<div>
{items.map(item => <div key={item.id}>{item.title}</div>)}
<div ref={sentinelRef} style={{ height: 1 }} />
</div>
);
}
优点
- 完全可定制
- 适用于任何滚动逻辑或 API
缺点
- 需要更多设置
- IntersectionObserver 在 IE11 中不受支持
实际应用模式和提示
- 防抖 API 请求以避免过度加载
- 显示加载动画或骨架屏以提升用户体验
- 添加重试逻辑处理网络错误
- 处理边缘情况如零结果或空页面
if (items.length === 0 && !hasMore) {
return <p>No posts found.</p>;
}
性能考虑
- 渲染成千上万个项目时使用
react-window
等库进行虚拟化 - 记忆化项目组件以防止重新渲染
- 正确清理观察者以避免内存泄漏
useEffect(() => {
const observer = new IntersectionObserver(...);
return () => observer.disconnect();
}, []);
结论
无限滚动是现代 UI 中的常见功能。无论你偏好第三方包的简便性还是自定义钩子的控制性,React 都使这两种方法变得易于实现。在实现无限滚动时,始终考虑性能和加载反馈。
常见问题
使用 `react-infinite-scroll-component` 等库可以提供快速便捷的设置。
当 API 返回空数组时,将 `hasMore` 标志设置为 false。
如果渲染太多项目,确实会影响性能。使用 `react-window` 等虚拟化工具来管理大型列表。
使用 `IntersectionObserver` 在底部的 `div`(哨兵元素)进入视图时触发数据加载。
Listen to your bugs 🧘, with OpenReplay
See how users use your app and resolve issues fast. Loved by thousands of developers