This hook lets you detect when a given element intersects with an ancestor element, or with the
document's viewport. It's using an IntersectionObserver
internally to observe changes in the
intersection.
This is useful when you need an element to behave differently when it's not viewed by the user.
#You can use this for:
- Pausing intensive animations (or other expensive processes) when they are off-screen.
- Creating a sidebar with items that are highlighted based on the currently viewed section of the page.
- Creating headers that change when the user scrolls.
- Lazy-loading of content as it becomes visible to the user.
#Implementation
Here's a basic implementation of this hook:
function useIntersectionObserver(target, options) {
const [intersecting, setIntersecting] = useState();
useEffect(() => {
const observer = new IntersectionObserver(
entries => {
setIntersecting(entries[0].isIntersecting);
},
options
);
observer.observe(target.current);
return () => observer.disconnect();
}, []);
return intersecting;
};
So what's going on here?
I create a IntersectionObserver
instance in a useEffect
with no dependencies (so it's
only called once, when the component mounts).
I then call observer.observe()
on the given target
element, and update the intersecting
state
inside the callback on the first entry (since I only observe one entry - our target
).
The hook returns a single boolean
value, representing whether the target is intersecting
the root
or not.
#Usage
Here's how you can use this hook:
const MyComponent = () => {
const target = useRef();
const intersecting = useIntersectionObserver(target, {
root: document.body,
rootMargin: '0px',
threshold: 0.5
});
return (
<div ref={target}>
{intersecting ? 'Visible' : 'Not visible'}
</div>
);
};
In the example above I observe intersections between a given div
and the viewport (i.e document.body
).
A threshold
of 0.5
means that the value of intersecting
becomes true
only when at least
50% of the element is inside the container.
#Live example
Scroll down/up to see the div change colors as it enters/leaves the container visible area (i'm using a threshold of 0.8 here)