在 React 中使用 Intersection Observer API 實現觸底加載分頁(無限滾動)
1.基本實現思路
- 在列表底部放置一個 哨兵元素(Sentinel)(如
<div>
)。 - 使用
IntersectionObserver
監聽該元素是否進入視口(即觸底)。 - 觸底時加載下一頁數據,并更新列表。
2.完整代碼實例
import { useState, useEffect, useRef } from 'react';function InfiniteScrollList() {const [data, setData] = useState([]); // 當前數據const [page, setPage] = useState(1); // 當前頁碼const [loading, setLoading] = useState(false); // 加載狀態const [hasMore, setHasMore] = useState(true); // 是否還有更多數據const sentinelRef = useRef(null); // 哨兵元素的 ref// 模擬異步加載數據const fetchData = async () => {if (loading || !hasMore) return;setLoading(true);// 模擬 API 請求(替換為實際接口)const mockData = Array.from({ length: 10 }, (_, i) => `Item ${(page - 1) * 10 + i + 1}`);// 模擬延遲await new Promise(resolve => setTimeout(resolve, 1000));setData(prev => [...prev, ...mockData]);setPage(prev => prev + 1);setLoading(false);// 假設第 5 頁后無數據if (page >= 5) setHasMore(false);};// 初始化 IntersectionObserveruseEffect(() => {if (!sentinelRef.current || !hasMore) return;const observer = new IntersectionObserver((entries) => {const [entry] = entries;if (entry.isIntersecting) {fetchData(); // 觸底時加載數據}},{ threshold: 1.0 } // 當哨兵元素完全進入視口時觸發);observer.observe(sentinelRef.current);return () => {if (sentinelRef.current) observer.unobserve(sentinelRef.current);};}, [page, hasMore, loading]); // 依賴項return (<div style={{ maxHeight: '400px', overflow: 'auto' }}><h2>無限滾動列表</h2><ul>{data.map((item, index) => (<li key={index} style={{ padding: '10px', borderBottom: '1px solid #eee' }}>{item}</li>))}</ul>{/* 哨兵元素:用于檢測觸底 */}<div ref={sentinelRef} style={{ height: '20px' }}>{loading && <p>加載中...</p>}{!hasMore && <p>沒有更多數據了</p>}</div></div>);
}export default InfiniteScrollList;
3.關鍵點說明
-
哨兵元素(Sentinel)
- 一個隱藏的
<div>
作為觸底標記,通過ref
綁定到IntersectionObserver
。
- 一個隱藏的
-
IntersectionObserver 配置
threshold: 1.0
:當哨兵元素100%
進入視口時觸發回調。- 在
useEffect
中初始化并清理觀察器,避免內存泄漏。
-
加載控制邏輯
loading
防止重復請求。hasMore
標記數據是否全部加載完畢。
-
性能優化
- 使用
useCallback
包裹 fetchData(如果函數邏輯復雜)。 - 實際項目中,結合分頁接口的
total
字段判斷是否還有數據。
- 使用
4.實際項目適配
- 替換 fetchData 中的模擬請求為真實 API 調用(如 axios 或 fetch)。
- 可加入防抖(Debounce)優化頻繁觸發問題(如快速滾動時)。