import { useCallback, useEffect, useRef, useState } from 'react'; interface UseInfiniteScrollOptions { /** * 是否有更多数据可以加载 */ hasNextPage: boolean; /** * 是否正在加载 */ isLoading: boolean; /** * 加载下一页的函数 */ fetchNextPage: () => void; /** * 触发加载的阈值(px),当距离容器底部多少像素时触发加载 */ threshold?: number; /** * 是否启用(默认为true) */ enabled?: boolean; } /** * 无限滚动Hook * 支持向下滚动和向上滚动加载更多 */ export function useInfiniteScroll({ hasNextPage, isLoading, fetchNextPage, threshold = 200, enabled = true, }: UseInfiniteScrollOptions) { const [isFetching, setIsFetching] = useState(false); const observerRef = useRef(null); const loadMoreRef = useRef(null); // 加载更多数据 const loadMore = useCallback(async () => { if (!hasNextPage || isLoading || isFetching) return; setIsFetching(true); try { fetchNextPage(); } finally { // 延迟重置状态,避免快速重复触发 setTimeout(() => { setIsFetching(false); }, 100); } }, [hasNextPage, isLoading, isFetching, fetchNextPage]); // 设置Intersection Observer useEffect(() => { if (!enabled || !loadMoreRef.current) return; const options = { root: null, rootMargin: `${threshold}px`, threshold: 0.1, }; observerRef.current = new IntersectionObserver( (entries) => { const [entry] = entries; if (entry.isIntersecting) { loadMore(); } }, options ); const currentRef = loadMoreRef.current; if (currentRef) { observerRef.current.observe(currentRef); } return () => { if (observerRef.current && currentRef) { observerRef.current.unobserve(currentRef); } }; }, [enabled, threshold, loadMore]); // 清理observer useEffect(() => { return () => { if (observerRef.current) { observerRef.current.disconnect(); } }; }, []); return { loadMoreRef, isFetching, loadMore, }; }