react实现触底加载
实现触底加载的基本思路
触底加载(Infinite Scroll)的核心逻辑是监听滚动事件,当页面滚动到接近底部时触发数据加载。React中可以通过结合useEffect和DOM事件监听实现。
监听滚动事件
在React组件中,通过useEffect添加滚动事件监听器,计算是否触达容器底部:
useEffect(() => {
const handleScroll = () => {
const { scrollTop, scrollHeight, clientHeight } = document.documentElement;
if (scrollTop + clientHeight >= scrollHeight - 10) {
// 触发加载更多数据
}
};
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, []);
使用Intersection Observer API(推荐)
更现代的方式是使用Intersection Observer API,性能更好且不依赖频繁的滚动计算:
useEffect(() => {
const observer = new IntersectionObserver(
(entries) => {
if (entries[0].isIntersecting) {
// 触发加载更多数据
}
},
{ threshold: 1.0 }
);
const sentinel = document.querySelector('#load-more-sentinel');
if (sentinel) observer.observe(sentinel);
return () => observer.disconnect();
}, []);
完整组件示例
import { useState, useEffect } from 'react';
function InfiniteScrollList() {
const [items, setItems] = useState([]);
const [loading, setLoading] = useState(false);
const [page, setPage] = useState(1);
const fetchData = async () => {
setLoading(true);
// 模拟API请求
const newItems = await fetch(`/api/items?page=${page}`);
setItems(prev => [...prev, ...newItems]);
setPage(prev => prev + 1);
setLoading(false);
};
useEffect(() => {
const observer = new IntersectionObserver(
([entry]) => entry.isIntersecting && !loading && fetchData(),
{ threshold: 1.0 }
);
observer.observe(document.querySelector('#sentinel'));
return () => observer.disconnect();
}, [loading]);
return (
<div>
{items.map(item => (
<div key={item.id}>{item.content}</div>
))}
<div id="sentinel" style={{ height: '20px' }} />
{loading && <div>Loading...</div>}
</div>
);
}
性能优化建议
- 添加防抖逻辑避免频繁触发
- 在卸载组件时清除事件监听
- 使用
useCallback缓存获取数据的函数 - 添加错误处理机制
- 考虑使用React Query等库管理数据状态
第三方库方案
现有成熟库可以快速实现该功能:
npm install react-infinite-scroll-component
使用示例:
import InfiniteScroll from 'react-infinite-scroll-component';
function List() {
return (
<InfiniteScroll
dataLength={items.length}
next={fetchData}
hasMore={true}
loader={<h4>Loading...</h4>}
>
{items.map(item => (
<div key={item.id}>{item.text}</div>
))}
</InfiniteScroll>
);
}






