DEV Community

GihanRangana
GihanRangana

Posted on

React useInView Hook - How to create useInView Hook in ReactJs

Learn how to create a powerful useInView hook in React using the Intersection Observer API. Elevate your development skills as we guide you through step-by-step instructions to create a custom useInView hook. Enhance user experience, optimize performance, and effortlessly track element visibility in your React projects.

Follow this video to step-by-step instructions

useInView.ts

import { useRef, useState, useEffect, RefObject } from 'react';

interface IOptions {
    root?: Element | null | undefined;
    rootMargin?: string,
    thresholds?: ReadonlyArray<number>
}

type useInViewType = {
    inView: boolean
    ref: RefObject<T> | null,
    observe: (element: RefObject<T>, callback: (entries: IntersectionObserverEntry[]) => void) => IntersectionObserver | null,
    unObserve: (observer: IntersectionObserver) => void
}

const useInView = (options: IOptions): useInViewType => {
    const [inView, setInView] = useState(false)

    const containerRef = useRef(null)

    const callback = (entries: IntersectionObserverEntry[]) => {
        const [entry] = entries
        setInView(entry.isIntersecting)
    }

    useEffect(() => {

        const _observer = new IntersectionObserver(callback, options)
        if (containerRef.current) _observer.observe(containerRef.current)

        return () => {
            if (containerRef.current) _observer.unobserve(containerRef.current)
        }

    }, [containerRef, options])

    // For Manual observers
    const observe = (element: RefObject<T>, callback: (entries: IntersectionObserverEntry[]) => void) => {
        const _observer = new IntersectionObserver(callback, options)
        containerRef.current = element.current

        return _observer
    }

    const unObserve = (observer: IntersectionObserver) => {
        if (containerRef.current) observer.unobserve(containerRef.current)
    }

    return {
        inView,
        ref: containerRef,
        observe,
        unObserve
    }

}

export default useInView;
Enter fullscreen mode Exit fullscreen mode

Usage:


function App() {

    const [data, setData] = useState<any[]>([])
    const [page, setPage] = useState(1)

    const { ref, inView } = useInView({ thresholds: [2] })

    useEffect(() => {
        setData([...new Array(5 * page)])
    }, [page])

    useEffect(() => {
        if (inView) setPage(prev => prev + 1)
    }, [inView])

    return (
        <div className={styles.mainContainer}>
            {data.map((row: any, index: number) => {
                return <Card key={`card-${index.toString()}`} index={index} />
            })}

            <button ref={ref} className={styles.loadMore}>Load More</button>
        </div>
    )
}
Enter fullscreen mode Exit fullscreen mode

Working Demo:

Top comments (0)