Easy Lazy Loading with React & Intersection Observer API

In this article, I’ll write a React hook and a React component that will help you achieve lazy loading in ReactJS.

What is Intersection Observer API?

Basically, Intersection Observer will monitor elements and check if they’re intersect with…


This content originally appeared on DEV Community and was authored by AnxinYang

In this article, I'll write a React hook and a React component that will help you achieve lazy loading in ReactJS.

What is Intersection Observer API?

Basically, Intersection Observer will monitor elements and check if they're intersect with the viewport of an document or, most of time, the browser viewport.

For more information, please refer to the MDN docs.

Create the React hook

First, let's start with an empty hook like this:

export function useIntersectionObserver(){
}

Then we can add a state that will tell us if the component is intersecting and return that state:

export function useIntersectionObserver(){
  const [isIntersecting, setIsIntersecting] = useState(false);
  return isIntersecting;
}

Now, we need a ref that can hold the observer:

export function useIntersectionObserver(){
  const [isIntersecting, setIsIntersecting] = useState(false);
  const observer = useRef<null | IntersectionObserver>(null);


  return isIntersecting;
}

Since we need a target element for the observer, let's add a parameter and state to the hook function:

export function useIntersectionObserver(ref: MutableRefObject<Element | null>){
  const [element, setElement] = useState<Element | null>(null);
  const [isIntersecting, setIsIntersecting] = useState(false);
  const observer = useRef<null | IntersectionObserver>(null);

  useEffect(() => {
        setElement(ref.current);
  }, [ref]);

  return isIntersecting;
}

Now, we can create a observer to observe the Element:

export function useIntersectionObserver(ref: MutableRefObject<Element | null>){
  const [element, setElement] = useState<Element | null>(null);
  const [isIntersecting, setIsIntersecting] = useState(false);
  const observer = useRef<null | IntersectionObserver>(null);

  useEffect(() => {
        setElement(ref.current);
  }, [ref]);

  useEffect(() => {
        if (!element) return;
        const ob = observer.current = new IntersectionObserver(([entry]) => {
            const isElementIntersecting = entry.isIntersecting;
            setIsIntersecting(isElementIntersecting);
        })
        ob.observe(element);
  }, [element])
  return isIntersecting;
}

And don't forget to disconnect the observer once the component is unmounted or the target element is changed.

export function useIntersectionObserver(ref: MutableRefObject<Element | null>){
  const [element, setElement] = useState<Element | null>(null);
  const [isIntersecting, setIsIntersecting] = useState(false);
  const observer = useRef<null | IntersectionObserver>(null);
  const cleanOb = () => {
        if (observer.current) {
            observer.current.disconnect()
        }
  }

  useEffect(() => {
        setElement(ref.current);
  }, [ref]);

  useEffect(() => {
        if (!element) return;
        cleanOb();
        const ob = observer.current = new IntersectionObserver(([entry]) => {
            const isElementIntersecting = entry.isIntersecting;
            setIsIntersecting(isElementIntersecting);
        })
        ob.observe(element);
        return () => {
            cleanOb()
        }
  }, [element])
  return isIntersecting;
}

Now, we want to be able to configure the observer, so let's add the options to the hook function as a parameter:

export function useIntersectionObserver(ref: MutableRefObject<Element | null>, options: IntersectionObserverInit = {}){
  const [element, setElement] = useState<Element | null>(null);
  const [isIntersecting, setIsIntersecting] = useState(false);
  const observer = useRef<null | IntersectionObserver>(null);
  const cleanOb = () => {
        if (observer.current) {
            observer.current.disconnect()
        }
  }

  useEffect(() => {
        setElement(ref.current);
  }, [ref]);

  useEffect(() => {
        if (!element) return;
        cleanOb();
        const ob = observer.current = new IntersectionObserver(([entry]) => {
            const isElementIntersecting = entry.isIntersecting;
            setIsIntersecting(isElementIntersecting);
        }, { ...options })
        ob.observe(element);
        return () => {
            cleanOb()
        }
  }, [element, options ])
  return isIntersecting;
}

For more information about the options, please refer to the MDN docs.

Last, since we usually don't want to remove the content we've rendered, let's add a parameter that allow us to choice if we want the observer to be disconnected after the target element is intersected.

export function useIntersectionObserver(ref: MutableRefObject<Element | null>, options: IntersectionObserverInit = {}, forward: boolean = true) {
    const [element, setElement] = useState<Element | null>(null);
    const [isIntersecting, setIsIntersecting] = useState(false);
    const observer = useRef<null | IntersectionObserver>(null);

    const cleanOb = () => {
        if (observer.current) {
            observer.current.disconnect()
        }
    }

    useEffect(() => {
        setElement(ref.current);
    }, [ref]);

    useEffect(() => {
        if (!element) return;
        cleanOb()
        const ob = observer.current = new IntersectionObserver(([entry]) => {
            const isElementIntersecting = entry.isIntersecting;
            if (!forward) {
                setIsIntersecting(isElementIntersecting)
            } else if (forward && !isIntersecting && isElementIntersecting) {
                setIsIntersecting(isElementIntersecting);
                cleanOb()
            };
        }, { ...options })
        ob.observe(element);
        return () => {
            cleanOb()
        }
    }, [element, options ])


    return isIntersecting;
}

Create a Lazy Loading Component

Once we have the hook we need, it's very simple to create a lazy loading componentwith it:


interface LazyLoadProps {
    tag?: ComponentType | keyof JSX.IntrinsicElements
    children: ReactNode
    style?: CSSProperties
    className?: string
    root?: Element | Document | null
    threshold?: number | number[]
    rootMargin?: string
    forward?: boolean
}

export function LazyLoad(props: LazyLoadProps) {
    const { tag = 'div', children, style, className } = props;
    const Tag: any = tag;
    const ref = useRef<Element>(null)
    const isIntersecting = useIntersectionObserver(ref, {
        root: props.root ?? null,
        threshold: props.threshold ?? 0,
        rootMargin: props.rootMargin
    }, props.forward);

    return (
        <Tag
            ref={ref}
            style={style}
            className={className}
            children={isIntersecting ? children : null}
        />
    )
}

And, here we go.

Thank you for reading this article. Please let me know if there is any issue I made.

The hook and the Lazyload component are included in my npm package ax-react-lib.


This content originally appeared on DEV Community and was authored by AnxinYang


Print Share Comment Cite Upload Translate Updates
APA

AnxinYang | Sciencx (2021-04-18T06:26:40+00:00) Easy Lazy Loading with React & Intersection Observer API. Retrieved from https://www.scien.cx/2021/04/18/easy-lazy-loading-with-react-intersection-observer-api/

MLA
" » Easy Lazy Loading with React & Intersection Observer API." AnxinYang | Sciencx - Sunday April 18, 2021, https://www.scien.cx/2021/04/18/easy-lazy-loading-with-react-intersection-observer-api/
HARVARD
AnxinYang | Sciencx Sunday April 18, 2021 » Easy Lazy Loading with React & Intersection Observer API., viewed ,<https://www.scien.cx/2021/04/18/easy-lazy-loading-with-react-intersection-observer-api/>
VANCOUVER
AnxinYang | Sciencx - » Easy Lazy Loading with React & Intersection Observer API. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2021/04/18/easy-lazy-loading-with-react-intersection-observer-api/
CHICAGO
" » Easy Lazy Loading with React & Intersection Observer API." AnxinYang | Sciencx - Accessed . https://www.scien.cx/2021/04/18/easy-lazy-loading-with-react-intersection-observer-api/
IEEE
" » Easy Lazy Loading with React & Intersection Observer API." AnxinYang | Sciencx [Online]. Available: https://www.scien.cx/2021/04/18/easy-lazy-loading-with-react-intersection-observer-api/. [Accessed: ]
rf:citation
» Easy Lazy Loading with React & Intersection Observer API | AnxinYang | Sciencx | https://www.scien.cx/2021/04/18/easy-lazy-loading-with-react-intersection-observer-api/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.