Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
April 18, 2021 06:26 am GMT

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 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.


Original Link: https://dev.to/anxinyang/easy-lazy-loading-with-react-intersection-observer-api-1dll

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To