Lazy Loading Data in ReactJS using Intersection Observer
Tags: react, javascript, webdev
Published: 2021-03-31 17:08:18

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](https://developer.mozilla.org/en-US/docs/Web/API/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.

```javascript
const useLazyLoad = (triggerRef, onGrabData, options) => {
  ...
}
```

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

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

    return () => {
      observer.disconnect()
    }
  }
}, [triggerRef, onIntersect, options])
```

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.

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

  if (intersectionRect.bottom - boundingRect.bottom <= 5) {
    onGrabData(...)
  }
}
```

## `entries`

The `entries` parameter is an array of [IntersectionObserverEntry](https://developer.mozilla.org/en-US/docs/Web/API/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.

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

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

{% codesandbox etk0i %}