DEV Community

Karl Castillo
Karl Castillo

Posted on • Edited on

Lazy Loading Data in ReactJS using Intersection Observer

Lazy loading is a way for websites to load new data for the user to see without needing to click a button.

No scroll events

Plenty of lazy load tutorials use scroll events to determine whether or not your user has scrolled to the end of a container. It's a valid solution to use scroll events but I was thinking of an alternative.

Say hello to the Intersection Observer API. The Intersection Observer keeps track of when elements intersect with the given container. This is what we'll use to determine whether or not we've hit the bottom of our container.

useLazyLoad Hook

The useLazyLoad hook will be where we'll use the Intersection Observer. The hook will have 3 parameters -- triggerRef, onGrabData, and options.

  • triggerRef is our ref to our trigger element
  • onGrabData is the function that will be called to load more data
  • options is the options object that can be passed to the Intersection Observer constructor.
const useLazyLoad = (triggerRef, onGrabData, options) => {
  ...
}
Enter fullscreen mode Exit fullscreen mode

Inside of a useEffect, we'll create our observer.

useEffect(() => {
  if (triggerRef.currrent) {
    const observer = new IntersectionObserver(onIntersect, options)
    observer.observe(triggerRef.current)

    return () => {
      observer.disconnect()
    }
  }
}, [triggerRef, onIntersect, options])
Enter fullscreen mode Exit fullscreen mode

A couple of important things to note here are onIntersect, observe() and disconnect().

  • onIntersect is a callback function called by the observer when the observed elements interact with the observer root.
  • observe is a function that makes an element something that the observer should be keeping track of.
  • disconnect is a cleanup function that stops the observer from observing.

onIntersect

The callback function required by the Intersection Observer receives entries as its parameter.

const onIntersect = (entries) => {
  const boundingRect = entries[0].boundingClientRect
  const intersectionRect = entries[0].intersectionRect

  if (intersectionRect.bottom - boundingRect.bottom <= 5) {
    onGrabData(...)
  }
}
Enter fullscreen mode Exit fullscreen mode

entries

The entries parameter is an array of IntersectionObserverEntry. The callback is called when one or more of the elements being observed intersects or stops intersecting against the root.

triggerRef

The triggerRef is an element that Intersection Observer will be keeping track of.

...
const triggerRef = useRef(null)
const { data } = useLayLoad(triggerRef, onGrabData, options)
...
return (
  <section>
    {data.map((item) => (
      <div key={item.id}>
        ...
      </div>
    ))}
    <div ref={triggerRef} />
  </section>
)
...
Enter fullscreen mode Exit fullscreen mode

The trigger element will be found beneath the data which will push the trigger beyond the viewport. When the user scrolls down, the trigger will intersect with the viewport triggering the intersection observer callback.

Working Demo

Top comments (2)

Collapse
 
zestzero profile image
Verthezest

Nice trick!. Not only for scroll event, but also applicable for many scenarios. Thank you!

Collapse
 
monfernape profile image
Usman Khalil

Good piece. Really liked your approach.