Files
jiaowu-test/src/components/InfiniteScroll/index.jsx

104 lines
2.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useEffect, useRef, useState, useCallback } from "react";
import { Empty, Spin } from "@arco-design/web-react";
import "./index.css";
const InfiniteScroll = ({
loadMore,
hasMore,
children,
threshold = 0.1, // 触发阈值,元素可见比例
className = "",
empty = false,
}) => {
const containerRef = useRef(null);
const sentinelRef = useRef(null);
const observerRef = useRef(null);
const throttleRef = useRef(null); // 节流控制
const [loading, setLoading] = useState(false);
const [hasInitialized, setHasInitialized] = useState(false); // 首次挂载
// 加载更多数据的处理函数(带节流)
const handleLoadMore = useCallback(() => {
if (loading || !hasMore) return;
// 节流处理500ms内只能触发一次
if (throttleRef.current) {
clearTimeout(throttleRef.current);
}
throttleRef.current = setTimeout(() => {
setLoading(true);
loadMore().finally(() => {
setLoading(false);
throttleRef.current = null;
});
}, 10);
}, [hasMore, loadMore, loading]);
// 设置IntersectionObserver
useEffect(() => {
// 初始化观察器
const options = {
root: containerRef.current,
rootMargin: "0px 0px 50px 0px", // 减少提前触发距离
threshold,
};
// 创建观察器实例
observerRef.current = new IntersectionObserver((entries) => {
const [entry] = entries;
if (entry.isIntersecting) {
handleLoadMore();
}
}, options);
// 开始观察哨兵元素
if (sentinelRef.current) {
observerRef.current.observe(sentinelRef.current);
}
// 初始加载检查 - 仅在组件首次挂载时执行
if (hasMore && !loading && !hasInitialized) {
setHasInitialized(true);
handleLoadMore();
}
// 清理函数
return () => {
if (observerRef.current) {
observerRef.current.disconnect();
}
// 清理节流定时器
if (throttleRef.current) {
clearTimeout(throttleRef.current);
throttleRef.current = null;
}
};
}, [loadMore, hasMore, threshold, loading, hasInitialized, handleLoadMore]);
return (
<div
ref={containerRef}
className={`infinite-scroll-container ${className}`}
>
{children}
{/* 哨兵元素 - 用于触发加载更多 */}
<div ref={sentinelRef} className="sentinel-element"></div>
{!hasMore && empty && (
<Empty description="暂无数据" className="empty-data" />
)}
{/* 滚动加载指示器 */}
{loading && hasMore && (
<div className="loading-container">
<Spin size={20} />
<span className="loading-text">加载中...</span>
</div>
)}
</div>
);
};
export default InfiniteScroll;