Utilizing Intersection Observer in React
Tags: webdev, javascript, react, programming
Published: 2024-04-17 08:16:07

Today, I want to talk about the `IntersectionObserver`, a built-in JavaScript API which is primarily used for lazy-loading images and implementing infinite scrolling. Creating an instance of this API is straightforward, as demonstrated below:

```
const observer = new IntersectionObserver(callback,options);
``` 

A `callback` function is required to trigger when intersecting entities change, while `options` is an object that defines the observer's behavior and policy. Utilizing this API enables the lazy loading of images in a React app. Refer to this [page](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API) for a comprehensive understanding of this API.

To utilize this API in a React application, you need to follow a few steps. 
Firstly Create a file called `Observer.js` which contains the `observer` object and its corresponding callback function. The structure of `Observer.js` will appear as follows:

```
export let observer;

export function initObserver() {
  observer = new IntersectionObserver(observerCallback);
}


function observerCallback(entries) {
  // TODO
}
```   

The `initObserver` function initializes the `observer` object, the body of the `observerCallback` function will be completed later. The `initObserver` needs to be called at the top level of the react tree, such as in `main.jsx`.

To render the images and enable observation, we need to create an `Image` component, which enables us to use the `observer`. The code snippet provided below demonstrates the implementation of the `Image` component.
```   
import React, { useEffect, useRef, useState } from "react";
import { observer } from "./Observer";

export default function Image({ src }) {
  const [isSeen, setIsSeen] = useState(false);
  const containerRef = useRef(null);


  useEffect(() => {
    if (containerRef.current) {
      containerRef.current.onObserve = () => {
        setIsSeen(true);
      };
      observer.observe(containerRef.current);
    }
    return () => observer.unobserve(containerRef.current);
  }, []);


  return isSeen ? (
    <img src={src} />
  ) : (
    <div
      ref={containerRef}
      style={{ width: "250px", height: "350px", backgroundColor: "black" }}
    />
  );
}
```

This component uses a prop named `src`, which represents the URL source of the image. Additionally, it requires a `ref` to reference an alternative element for the image which is called `containerRef`. In the example given, a basic `div` with a black background is used, but you have the flexibility to customize this element according to your specific requirements.

In the `useEffect` setup function, we first check if the `containerRef` has a reference. If it has, we add a function called `onObserve` to it. Inside this function, we change the component state `isSeen` to true, indicating that the image should be rendered and displayed. Then, we make the `observer` intersects the `containerRef`. Finally, in the clean-up function of the `useEffect`, we remove the `containerRef` from the list of observed entries by calling the `unobserve` function.

Now, moving on to the final step, we will go back to the `Observer.js` file and proceed with initializing the body of the `observerCallback` function. It should verify if an entry intersects and then invoke its `onObserve` function. Therefore, the updated code will appear as follows:

```
function observerCallback(entries) {
    entries.forEach((entry) => {
      if (entry.isIntersecting) {
        entry.target.onObserve();
      }
    });
  }
```

Thank you for taking the time to read this article. I hope you found it helpful. If you have any questions or comments, please share them with me.