Intersection Observer API in React
The Intersection Observer is a browser API that allows developers to detect when an element on the page enters or leaves the viewport (the visible part of the browser window) or a specific container. This API is especially useful for implementing features like lazy loading of images, playing videos only when they are visible, or animations that trigger on page scroll.
How Intersection Observer Works
Observer Creation: An IntersectionObserver is instantiated by passing a callback and an options object.
The callback is executed every time one of the observed elements enters or leaves the visibility zone.
Observation of Elements: Methods such as observe are used to start observing an element and unobserve to stop observing it.
Advantages of Intersection Observer
Efficiency: More efficient than constantly listening to scroll events and manually checking element positions.
Simplicity: Makes the code simpler and cleaner.
Compatibility: Supports most modern browsers.
React example
Using the Intersection Observer, we will make an animated list, this will show an animation when the user scrolls to a part that was not visible on the screen yet.
The list is long, but only what the window allows the user to see will be visible on the screen, everything else will be hidden
AnimatedList.js
import React, { useEffect, useRef, useState } from "react";
import "./animatedList.css";
const AnimatedList = ({ items }) => {
const [visibleItems, setVisibleItems] = useState(new Set());
const listRef = useRef([]);
useEffect(() => {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
const index = Number(entry.target.dataset.index);
if (entry.isIntersecting) {
setVisibleItems(
(prevVisibleItems) => new Set(prevVisibleItems.add(index))
);
} else {
setVisibleItems((prevVisibleItems) => {
const newVisibleItems = new Set(prevVisibleItems);
newVisibleItems.delete(index);
return newVisibleItems;
});
}
});
},
{ threshold: 0.1 }
);
listRef.current.forEach((item) => {
if (item) observer.observe(item);
});
return () => {
listRef.current.forEach((item) => {
if (item) observer.unobserve(item);
});
};
}, [items]);
return (
<ul>
{items.map((item, index) => (
<li
key={index}
data-index={index}
ref={(el) => (listRef.current[index] = el)}
className={`list-item ${visibleItems.has(index) ? "visible" : ""}`}
>
{visibleItems.has(index) && item}
</li>
))}
</ul>
);
};
export default AnimatedList;
Maintains a visibleItems state, which is an array containing the indexes of the visible items.
Use useRef to create references to list items.
Use useEffect to create an Intersection Observer that observes each element in the list.
Updates the visibleItems state to add or remove items based on their visibility.
Renders all <li> elements, but only displays the contents of those in the visibleItems set.
animatedList.css
ul {
padding: 0;
}
.list-item {
list-style-type: none;
opacity: 0;
transform: translateY(20px);
transition: opacity 0.5s, transform 0.5s;
margin: 10px 0;
padding: 20px;
background-color: #f0f0f0;
border: 1px solid #ddd;
border-radius: 5px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
}
.list-item.visible {
opacity: 1;
transform: translateY(0);
}
App.js
import React from "react";
import AnimatedList from "./components/animatedList";
const App = () => {
const items = Array.from({ length: 120 }, (_, index) => `Item ${index + 1}`);
return (
<div>
<h1>Intersection Observer API in React</h1>
<p>Scroll down to see the animations......</p>
<AnimatedList items={items} />
</div>
);
};
export default App;