│ │ │ │ │ │ - 监听容器滚动事件 │ │ │ │ - 距离底部阈值可配置(默认200px) │ │ │ │ - 自动触发onLoadMore回调 │ │ │ │ - 支持加载状态管理
89 lines
2.5 KiB
JavaScript
89 lines
2.5 KiB
JavaScript
// src/views/Community/components/DynamicNewsCard/hooks/useInfiniteScroll.js
|
||
// 无限滚动 Hook
|
||
|
||
import { useEffect, useRef, useCallback } from 'react';
|
||
|
||
/**
|
||
* 无限滚动 Hook
|
||
* 监听容器滚动事件,当滚动到底部附近时触发加载更多数据
|
||
*
|
||
* @param {Object} options - 配置选项
|
||
* @param {Function} options.onLoadMore - 加载更多回调函数(返回 Promise)
|
||
* @param {boolean} options.hasMore - 是否还有更多数据
|
||
* @param {boolean} options.isLoading - 是否正在加载
|
||
* @param {number} options.threshold - 触发阈值(距离底部多少像素时触发,默认200px)
|
||
* @returns {Object} { containerRef } - 容器引用
|
||
*/
|
||
export const useInfiniteScroll = ({
|
||
onLoadMore,
|
||
hasMore = true,
|
||
isLoading = false,
|
||
threshold = 200
|
||
}) => {
|
||
const containerRef = useRef(null);
|
||
const isLoadingRef = useRef(false);
|
||
|
||
// 滚动处理函数
|
||
const handleScroll = useCallback(() => {
|
||
const container = containerRef.current;
|
||
|
||
// 检查条件:容器存在、未加载中、还有更多数据
|
||
if (!container || isLoadingRef.current || !hasMore) {
|
||
return;
|
||
}
|
||
|
||
const { scrollTop, scrollHeight, clientHeight } = container;
|
||
const distanceToBottom = scrollHeight - scrollTop - clientHeight;
|
||
|
||
// 距离底部小于阈值时触发加载
|
||
if (distanceToBottom < threshold) {
|
||
console.log(
|
||
'%c⬇️ [懒加载] 触发加载下一页',
|
||
'color: #8B5CF6; font-weight: bold;',
|
||
{
|
||
scrollTop,
|
||
scrollHeight,
|
||
clientHeight,
|
||
distanceToBottom,
|
||
threshold
|
||
}
|
||
);
|
||
|
||
isLoadingRef.current = true;
|
||
|
||
// 调用加载函数并更新状态
|
||
onLoadMore()
|
||
.then(() => {
|
||
console.log('%c✅ [懒加载] 加载完成', 'color: #10B981; font-weight: bold;');
|
||
})
|
||
.catch((error) => {
|
||
console.error('%c❌ [懒加载] 加载失败', 'color: #DC2626; font-weight: bold;', error);
|
||
})
|
||
.finally(() => {
|
||
isLoadingRef.current = false;
|
||
});
|
||
}
|
||
}, [onLoadMore, hasMore, threshold]);
|
||
|
||
// 绑定滚动事件
|
||
useEffect(() => {
|
||
const container = containerRef.current;
|
||
if (!container) return;
|
||
|
||
// 添加滚动监听
|
||
container.addEventListener('scroll', handleScroll, { passive: true });
|
||
|
||
// 清理函数
|
||
return () => {
|
||
container.removeEventListener('scroll', handleScroll);
|
||
};
|
||
}, [handleScroll]);
|
||
|
||
// 更新 loading 状态的 ref
|
||
useEffect(() => {
|
||
isLoadingRef.current = isLoading;
|
||
}, [isLoading]);
|
||
|
||
return { containerRef };
|
||
};
|