58 lines
1.2 KiB
TypeScript
58 lines
1.2 KiB
TypeScript
import { useEffect, useRef, useState } from 'react'
|
|
|
|
interface UseInfiniteScrollOptions {
|
|
hasMore: boolean
|
|
loading: boolean
|
|
onLoadMore: () => void
|
|
threshold?: number
|
|
}
|
|
|
|
export function useInfiniteScroll({
|
|
hasMore,
|
|
loading,
|
|
onLoadMore,
|
|
threshold = 200,
|
|
}: UseInfiniteScrollOptions): React.RefObject<HTMLDivElement | null> {
|
|
const observerRef = useRef<HTMLDivElement>(null)
|
|
const [isIntersecting, setIsIntersecting] = useState(false)
|
|
|
|
useEffect(() => {
|
|
if (!hasMore || loading) {
|
|
return
|
|
}
|
|
|
|
const observer = new IntersectionObserver(
|
|
(entries) => {
|
|
const entry = entries[0]
|
|
if (entry?.isIntersecting) {
|
|
setIsIntersecting(true)
|
|
} else {
|
|
setIsIntersecting(false)
|
|
}
|
|
},
|
|
{
|
|
rootMargin: `${threshold}px`,
|
|
}
|
|
)
|
|
|
|
const currentRef = observerRef.current
|
|
if (currentRef) {
|
|
observer.observe(currentRef)
|
|
}
|
|
|
|
return () => {
|
|
if (currentRef) {
|
|
observer.unobserve(currentRef)
|
|
}
|
|
}
|
|
}, [hasMore, loading, threshold])
|
|
|
|
useEffect(() => {
|
|
if (isIntersecting && hasMore && !loading) {
|
|
onLoadMore()
|
|
}
|
|
}, [isIntersecting, hasMore, loading, onLoadMore])
|
|
|
|
return observerRef
|
|
}
|