DEV Community

Cover image for Use the IntersectionObserver API in React
Phuoc Nguyen
Phuoc Nguyen

Posted on • Originally published at phuoc.ng

Use the IntersectionObserver API in React

In our previous post, we covered the basics of the IntersectionObserver API, including its syntax and common methods. The good news is that we don't need to rely on any external libraries to use this API with React. By using the built-in React hooks, we can handle all the intersection observer functionality with ease.

In this post, we'll dive into how to use the IntersectionObserver API in React.

Checking if an element is partially visible

When working with React, it's not recommended to use DOM manipulation methods like querySelector and querySelectorAll to retrieve elements. Instead, we can use the useRef hook.

If you're new to useRef, I recommend checking out this series which covers basic and advanced usage of React refs with real-world examples.

To start, we create a ref using the useRef hook to reference the specific DOM element we want to observe for visibility changes. We attach the target element to the ref using the ref attribute.

Then, we set up a state variable called isVisible using the useState hook with an initial value of false.

const elementRef = React.useRef();
const [isVisible, setIsVisible] = React.useState(false);

// Render
return (
    <div ref={elementRef}>...</div>
);
Enter fullscreen mode Exit fullscreen mode

Next, we're going to use the useEffect hook to set up an IntersectionObserver. This is a browser API that lets us know when an element becomes visible or hidden on the page. But before we can use the API, we need to get the element we want to observe.

To do this, we used the useRef hook to create a reference to the element. Then, we access the current property of the reference to get the actual element.

Once we have the element, we create an observer instance using the IntersectionObserver constructor. We pass in a callback function that will be called whenever the element intersects with the viewport.

The callback function receives an array of entries, but we only care about the first one. So, we use array destructuring to get it. This entry object contains information about whether or not the element is currently visible.

If the element is visible, we set our isVisible state variable to true. If it's not visible, we set isVisible to false. Finally, we call the observer's observe method with our target element.

To clean up after ourselves, we return a function from the useEffect hook that calls the observer's unobserve method on our target element. This ensures that when our component unmounts or the element is removed from the page, we stop observing it.

Here's an example of how to use the useEffect hook to set up an IntersectionObserver.

React.useEffect(() => {
    const ele = elementRef.current;
    if (!ele) {
        return;
    }

    const observer = new IntersectionObserver(([entry]) => {
        setIsVisible(entry.isIntersecting);
    });
    observer.observe(ele);

    return () => {
        observer.unobserve(ele);
    };
}, []);
Enter fullscreen mode Exit fullscreen mode

Lastly, we create a div element that shows whether or not the observed element is partially visible.

// Render
return (
    <div>
        {isVisible ? 'Element is partially visible' : 'Element is not partially visible'}
    </div>
);
Enter fullscreen mode Exit fullscreen mode

Check out the demo below. Scroll up and down to see how the message updates automatically, letting you know if the target element is visible or not.

Checking if an element is fully visible

To ensure that an element is fully visible, we can update our implementation using the intersectionRatio property of the entry object passed to the IntersectionObserver's callback function.

The intersectionRatio property is a value between 0 and 1 that represents the percentage of the targeted element that is currently visible within the viewport. If this value is equal to 1, then the entire element is visible.

To achieve this, we can modify our existing code by checking whether entry.intersectionRatio is equal to 1 or not. If it's equal to 1, then we can set our state variable isVisible to true, indicating that the element is fully visible. Otherwise, we set it to false.

Here's an example implementation:

React.useEffect(() => {
    const ele = elementRef.current;
    if (!ele) {
        return;
    }

    const observer = new IntersectionObserver(([entry]) => {
        setIsVisible(entry.intersectionRatio === 1);
    });
    observer.observe(ele);

    return () => {
        observer.unobserve(ele);
    };
}, []);
Enter fullscreen mode Exit fullscreen mode

Although it may seem promising and workable in theory, in practice, it falls short. Go ahead and try scrolling down in the playground below. You'll notice that the message doesn't update even if you scroll over the entire element.

The reason why the approach to check if an element is fully visible doesn't work is because intersectionRatio only considers when the element is partially or not visible at all.

We can fix this issue by adding an extra option to the IntersectionObserver constructor. This option is called threshold, and it lets us specify at what percentage of visibility we want our callback function to be executed.

To make sure our callback function is called when the observed element is fully visible, we can set the threshold value to 1. This means that our callback function will only be executed when the entire observed element is visible in the viewport.

It's worth noting that the threshold option can also accept an array of numbers. Here's how we can modify our existing code to implement this solution:

React.useEffect(() => {
    const ele = elementRef.current;
    if (!ele) {
        return;
    }

    const observer = new IntersectionObserver(([entry]) => {
        setIsVisible(entry.isIntersecting);
    }, {
        threshold: 1,
    });
    observer.observe(ele);

    return () => {
        observer.unobserve(ele);
    };
}, []);
Enter fullscreen mode Exit fullscreen mode

With this modification, the isVisible state variable now accurately shows whether or not the observed element is fully visible. Give it a try by scrolling up and down in the playground below to see how this updated implementation works.

Conclusion

In this post, we learned how to use the IntersectionObserver API with React. By encapsulating all the intersection observer functionality within the useEffect hook, we were able to check if an element is partially or fully visible using different threshold values.

Using the IntersectionObserver API can significantly improve an application's performance by reducing the number of calculations required to determine visibility changes. In our next post, we'll take it a step further and learn how to turn the implementation into a reusable hook and component.


If you want more helpful content like this, feel free to follow me:

Top comments (0)