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