useIntersectionObserver
function useIntersectionObserver<E extends HTMLElement>(
options: IntersectionObserverHookOptions<E>
): RefCallback<E>;
The useIntersectionObserver
hook can be used to interact with the
Intersection Observer API
within React components.
Example Usage
This example was forked from the Mozilla Simple Example and a demo is available here.
"use client";
import { Typography } from "@react-md/core/typography/Typography";
import { useIntersectionObserver } from "@react-md/core/useIntersectionObserver";
import { type ReactElement, useCallback, useState } from "react";
import styles from "./SimpleExample.module.scss";
const numSteps = 20;
const thresholds = Array.from({ length: numSteps }, (_, i) => i / numSteps);
thresholds.push(0);
const INCREASING = "rgba(40, 40, 190, ratio)";
const DECREASING = "rgba(190, 40, 40, ratio)";
export default function SimpleExample(): ReactElement {
const [{ ratio, increasing }, setState] = useState({
ratio: 0.0,
increasing: true,
});
const targetRef = useIntersectionObserver({
threshold: thresholds,
rootMargin: "0px",
onUpdate: useCallback(([entry]) => {
const { intersectionRatio } = entry;
setState((prevState) => {
return {
ratio: intersectionRatio,
increasing: intersectionRatio > prevState.ratio,
};
});
}, []),
});
return (
<>
<Typography type="headline-4" margin="none" textAlign="center">
Scroll Down
</Typography>
<div className={styles.container}>
<div
ref={targetRef}
className={styles.box}
style={{
backgroundColor: (increasing ? INCREASING : DECREASING).replace(
"ratio",
`${ratio}`,
),
}}
>
<div className={styles.vertical}>
Welcome to <strong>The Box!</strong>
</div>
</div>
</div>
</>
);
}
Parameters
options
- An object with the following definition:
export interface IntersectionObserverHookOptions<E extends HTMLElement> {
/**
* An optional ref to merge with the ref returned by this hook.
*/
ref?: Ref<E>;
/**
* **Must be wrapped in `useCallback` to prevent re-creating the
* IntersectionObserver each render.**
*/
onUpdate: (entries: readonly IntersectionObserverEntry[]) => void;
/**
* **Must be wrapped in `useCallback` to prevent re-creating the
* IntersectionObserver each render.**
*
* If this is defined, the `ref` will be ignored along with the returned
* ref.
*/
getTargets?: () => readonly Element[];
/**
* This is the same as the normal `root` for an IntersectionObserverInit, but
* also supports refs.
*/
root?: RefObject<IntersectionObserverRoot> | IntersectionObserverRoot;
/**
* Set this to `true` if the intersection observer behavior should be
* disabled.
*
* @defaultValue `false`
*/
disabled?: boolean;
/**
* **When using a list of thresholds, they must either be defined outside of
* the component or wrapped in a `useMemo` to prevent the IntersectionObserver
* from being re-created each render.**
*
* @see {@link getThreshold}
*/
threshold?: IntersectionObserverThreshold;
/** @see {@link getRootMargin} */
rootMargin?: IntersectionObserverRootMargin;
/**
* **Must be wrapped in `useCallback` to prevent re-creating the
* IntersectionObserver each render.**
*
* This can be used to dynamically generate the `threshold` which is
* generally useful if you need access to the DOM or do some expensive
* computation.
*
* If this option is provided, `threshold`'s value will be ignored.
*/
getThreshold?: () => IntersectionObserverThreshold;
/**
* **Must be wrapped in `useCallback` to prevent re-creating the
* IntersectionObserver each render.**
*
* This can be used to dynamically generate the `rootMargin` which is
* generally useful if you need access to the DOM.
*
* Note: If this option is provided, `rootMargin` will be ignored.
*/
getRootMargin?: () => IntersectionObserverRootMargin;
}
getTargets
The getTargets
option can be used to track multiple elements. When this
option is provided, the hook ignores the value of the returned ref.
This is used for the useActiveHeadingId hook.
threshold
The threshold
option can be used to define the intersection threshold. This
value needs to be memoized to prevent re-creating the Intersection Observer
during each render.
// Memoized by not having it during the render cycle
const threshold = [0, 0.25, 0.5, 0.75, 1];
function Example() {
const targetRef = useIntersectionObserver({
threshold,
onUpdate: useCallback(([entry]) => {
// do something
}, []),
});
}
// or just use useMemo
function Example() {
const targetRef = useIntersectionObserver({
threshold: useMemo(() => [0, 0.25, 0.5, 0.75, 1]),
onUpdate: useCallback(([entry]) => {
// do something
}, []),
});
}
getThreshold
This option can be used to dynamically generate a threshold and will override
the threshold
value.
const targetRef = useIntersectionObserver({
getThreshold: useCallback(() => {
// pretend some expensive computation
return [0, 0.25, 0.5, 0.75, 1];
}, []),
onUpdate: useCallback(() => {
// do something
}, []),
});
getRootMargin
This option can be used to dynamically generate the rootMargin
.
const nodeRef = useRef<HTMLElement>();
const targetRef = useIntersectionObserver({
getRootMargin: useCallback(() => {
return `-${nodeRef.current.offsetHeight - 1}px 0px 0px`;
}, []),
onUpdate: useCallback(() => {
// do something
}, []),
});
Returns
A RefCallback
that needs to be passed to the DOM node to watch.