React实现触底加载更多
时间: 2025-07-02 20:34:28 浏览: 4
### React 中实现触底加载更多的解决方案
在 React 开发中,实现触底加载更多是一种常见的需求。以下是几种主流方法及其对应的示例代码。
#### 方法一:基于 `antd-mobile` 的 `InfiniteScroll` 组件
通过使用 `antd-mobile` 提供的 `InfiniteScroll` 和 `List` 组件,可以轻松实现触底加载功能[^1]。
```jsx
import React, { useState } from 'react';
import { InfiniteScroll, List } from 'antd-mobile';
const TouchBottomLoadMore = ({ list }) => {
const [len, setLen] = useState(10);
const loadMore = () => {
setTimeout(() => {
setLen(len + 10);
}, 1000);
};
return (
<>
<List>
<div>
{list.slice(0, len).map((item, index) => (
<span key={index}>{item}</span>
))}
</div>
</List>
<InfiniteScroll loadMore={loadMore} hasMore={len < list.length}>
Loading more...
</InfiniteScroll>
</>
);
};
export default TouchBottomLoadMore;
```
---
#### 方法二:自定义滚动事件监听器
通过监听 DOM 容器的 `scroll` 事件并计算其位置,可以在不依赖第三方库的情况下实现触底检测逻辑[^3][^4]。
```jsx
import React, { useEffect, useRef, useState } from 'react';
const ScrollBasedTouchBottom = () => {
const containerRef = useRef(null);
const [data, setData] = useState(Array.from({ length: 20 }).map((_, i) => `Item ${i + 1}`));
const [loading, setLoading] = useState(false);
useEffect(() => {
const handleScroll = () => {
if (!containerRef.current || loading) return;
const { scrollTop, clientHeight, scrollHeight } = containerRef.current;
if (scrollTop + clientHeight >= scrollHeight - 10 && !loading) {
fetchMoreData();
}
};
containerRef.current?.addEventListener('scroll', handleScroll);
return () => containerRef.current?.removeEventListener('scroll', handleScroll);
}, [loading]);
const fetchMoreData = async () => {
setLoading(true);
await new Promise(resolve => setTimeout(resolve, 1000)); // Simulate API call delay
setData(prevData => [
...prevData,
...Array.from({ length: 10 }).map((_, i) => `Item ${(prevData.length + i + 1)}`)
]);
setLoading(false);
};
return (
<div ref={containerRef} style={{ overflowY: 'auto', maxHeight: '400px' }}>
{data.map(item => (
<div key={item} style={{ padding: '8px', borderBottom: '1px solid #ccc' }}>
{item}
</div>
))}
{loading && <div>Loading...</div>}
</div>
);
};
export default ScrollBasedTouchBottom;
```
---
#### 方法三:结合 Hook 使用 `useInViewPort`
借助自定义 Hook 或者现有的工具函数(如 `useInViewPort`),可以通过判断目标元素是否进入视口范围来触发加载行为[^5]。
```jsx
import React, { useState, useCallback, useRef } from 'react';
import useInViewport from './hooks/useInViewport'; // 自定义 hook 文件路径需调整
const InViewPortTouchBottom = () => {
const containerRef = useRef(null);
const inViewPort = useInViewport(containerRef);
const [offset, setOffset] = useState(0);
const [noticeData, setNoticeData] = useState({
list: Array.from({ length: 20 }).map((_, i) => `Item ${i + 1}`),
total: 100
});
const [loading, setLoading] = useState(false);
const LIMIT = 10;
const loadMore = useCallback(async () => {
if (loading || noticeData.list.length >= noticeData.total) return;
setLoading(true);
await new Promise(resolve => setTimeout(resolve, 1000)); // 模拟异步请求延迟
const newData = Array.from({ length: LIMIT }).map(
(_, i) => `Item ${(noticeData.list.length + i + 1)}`
);
setNoticeData(prevData => ({
...prevData,
list: [...prevData.list, ...newData]
}));
setLoading(false);
}, [loading, noticeData]);
// 当元素进入视口时自动加载更多
useUpdateEffect(() => {
if (inViewPort) {
setOffset(offset + LIMIT);
loadMore();
}
}, [inViewPort]);
return (
<div ref={containerRef} style={{ border: '1px solid gray', minHeight: '200px' }}>
{noticeData.list.map(item => (
<div key={item} style={{ padding: '8px', borderBottom: '1px solid #ccc' }}>
{item}
</div>
))}
{loading ? <div>Loading...</div> : null}
{noticeData.list.length >= noticeData.total && <div>No More Data</div>}
</div>
);
};
export default InViewPortTouchBottom;
```
---
#### 性能优化建议
为了提升用户体验和性能,在开发过程中需要注意以下几点:
- **防抖/节流**:对于高频触发的操作(如滚动事件),应采用防抖或节流技术以降低回调频率。
- **虚拟化列表**:当数据量较大时,推荐使用 `react-window` 或 `react-virtualized` 等库进行虚拟化渲染[^2]。
- **状态管理**:合理设计组件的状态结构,避免不必要的重新渲染。
---
阅读全文
相关推荐



















