react滚动加载实现
实现滚动加载的基本思路
滚动加载的核心是通过监听滚动事件,判断用户是否滚动到页面底部附近,触发数据加载。React中可以通过结合useEffect和DOM事件监听实现。
监听滚动事件
在React组件中,通过useEffect添加滚动事件监听器。需要计算滚动位置和容器高度的关系:
useEffect(() => {
const handleScroll = () => {
const { scrollTop, scrollHeight, clientHeight } = document.documentElement;
if (scrollTop + clientHeight >= scrollHeight - 5) {
// 触发加载更多数据
}
};
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 () => {
if (sentinel) observer.unobserve(sentinel);
};
}, []);
数据加载逻辑
实现异步数据加载函数,通常与API调用结合:
const [items, setItems] = useState([]);
const [page, setPage] = useState(1);
const [loading, setLoading] = useState(false);
const loadMoreItems = async () => {
if (loading) return;
setLoading(true);
const newItems = await fetchData(page);
setItems(prev => [...prev, ...newItems]);
setPage(prev => prev + 1);
setLoading(false);
};
性能优化建议
避免频繁触发滚动事件,可以使用防抖(debounce)技术:
const debouncedHandleScroll = debounce(handleScroll, 100);
window.addEventListener('scroll', debouncedHandleScroll);
添加加载状态提示和错误处理:
{loading && <div>Loading more items...</div>}
{error && <div>Error loading items</div>}
完整组件示例
import React, { useState, useEffect } from 'react';
const InfiniteScrollList = () => {
const [items, setItems] = useState([]);
const [page, setPage] = useState(1);
const [loading, setLoading] = useState(false);
const [hasMore, setHasMore] = useState(true);
const fetchData = async (pageNum) => {
// 模拟API调用
return new Array(10).fill(0).map((_, i) =>
`Item ${(pageNum - 1) * 10 + i + 1}`
);
};
const loadMoreItems = async () => {
if (loading || !hasMore) return;
setLoading(true);
try {
const newItems = await fetchData(page);
setItems(prev => [...prev, ...newItems]);
setPage(prev => prev + 1);
setHasMore(newItems.length > 0);
} catch (err) {
console.error('Failed to load items:', err);
} finally {
setLoading(false);
}
};
useEffect(() => {
const observer = new IntersectionObserver(
(entries) => {
if (entries[0].isIntersecting) {
loadMoreItems();
}
},
{ threshold: 1.0 }
);
const sentinel = document.querySelector('#load-more-sentinel');
if (sentinel) observer.observe(sentinel);
return () => {
if (sentinel) observer.unobserve(sentinel);
};
}, [loading, hasMore]);
return (
<div>
{items.map((item, index) => (
<div key={index} style={{ padding: '20px', border: '1px solid #ccc' }}>
{item}
</div>
))}
<div id="load-more-sentinel" style={{ height: '20px' }} />
{loading && <div>Loading...</div>}
{!hasMore && <div>No more items</div>}
</div>
);
};
export default InfiniteScrollList;
第三方库方案
对于复杂场景,可以考虑使用现成的React无限滚动库:

- react-infinite-scroll-component
- react-window + react-window-infinite-loader
- react-virtualized
这些库提供了更完善的API和性能优化,适合处理大量数据的情况。






