useInfiniteScroll
Automatically trigger a callback when an element scrolls into view.
Endless Gallery
Scroll down to load more colors
0 Items Loaded
Usage
import { useState } from 'react'
import { useInfiniteScroll } from 'usehooks-ts'
export default function App() {
const [items, setItems] = useState([1, 2, 3])
const [loading, setLoading] = useState(false)
const loadMore = () => {
if (loading) return
setLoading(true)
setTimeout(() => {
setItems(prev => [...prev, prev.length + 1])
setLoading(false)
}, 1000)
}
const [loaderRef] = useInfiniteScroll(loadMore)
return (
<div>
{items.map(i => <div key={i}>{i}</div>)}
<div ref={loaderRef}>{loading ? 'Loading...' : 'Scroll for more'}</div>
</div>
)
}API
const [loaderRef, isVisible] = useInfiniteScroll(callback, options?)Parameters
| Name | Type | Default | Description |
|---|---|---|---|
callback | () => void | - | Function to call when visible |
options | UseInfiniteScrollOptions | { threshold: 0.5 } | IntersectionObserver options |
Return Values
| Name | Type | Description |
|---|---|---|
loaderRef | RefObject | Ref to attach to the target element |
isVisible | boolean | Whether the element is currently visible |
Hook
import { useEffect, useRef, useState, useCallback } from 'react'
export interface UseInfiniteScrollOptions {
threshold?: number | number[]
root?: Element | Document | null
rootMargin?: string
}
export function useInfiniteScroll(
callback: () => void,
options: UseInfiniteScrollOptions = {},
) {
const [isIntersecting, setIntersecting] = useState(false)
const observerRef = useRef<IntersectionObserver | null>(null)
const elementRef = useRef<HTMLElement | null>(null)
const handleIntersect = useCallback(
(entries: IntersectionObserverEntry[]) => {
const entry = entries[0]
setIntersecting(entry.isIntersecting)
if (entry.isIntersecting) {
callback()
}
},
[callback],
)
useEffect(() => {
const { threshold = 0.5, root = null, rootMargin = '0px' } = options
observerRef.current = new IntersectionObserver(handleIntersect, {
threshold,
root,
rootMargin,
})
const currentElement = elementRef.current
if (currentElement) {
observerRef.current.observe(currentElement)
}
return () => {
if (observerRef.current) {
observerRef.current.disconnect()
}
}
}, [handleIntersect, options])
return [elementRef, isIntersecting] as const
}