Skip to content

Instantly share code, notes, and snippets.

@nicolaschenet
Last active October 12, 2020 11:36
Show Gist options
  • Save nicolaschenet/043fc3742837f284411c6fd7254dcb86 to your computer and use it in GitHub Desktop.
Save nicolaschenet/043fc3742837f284411c6fd7254dcb86 to your computer and use it in GitHub Desktop.
/** Given a specific ref, returns the Intersection information */
import * as React from 'react';
import type {
UseIntersectionOptions,
UseIntersectionReturnValue
} from './useIntersectionObserver.types.ts';
export const useIntersection = (
target?: Element | null,
options?: UseIntersectionOptions
): UseIntersectionReturnValue => {
const intersectionObserverOptions = {
root: null,
rootMargin: '0px,
threshold: 1,
...options
};
const [isVisible, setIsVisible] = React.useState<boolean>(false);
const [intersectionRatio, setIntersectionRatio] = React.useState<number>(0);
const observer = React.useMemo(() => {
const handleIntersection: IntersectionObserverCallback = entries => {
const entry = entries
// We need to sort the entries in a way, we would then find the
// latest entry involving our element, otherwise we could use
// outdated value.
.sort((a, b) => b.time - a.time)
.find(
(event: IntersectionObserverEntry) =>
event.target === target
);
if (entry) {
const {
isIntersecting,
intersectionRatio: entryIntersectionRatio
} = entry;
setIsVisible(isIntersecting);
setIntersectionRatio(entryIntersectionRatio);
}
};
return new IntersectionObserver(handleIntersection, {
root: intersectionObserverOptions.root,
rootMargin: intersectionObserverOptions.rootMargin,
threshold: intersectionObserverOptions.threshold
});
// This weirdly exhaustive dependency declaration avoids an infinite loop
// that would occur if we just used `intersectionObserverOptions`
}, [
intersectionObserverOptions.root,
intersectionObserverOptions.rootMargin,
intersectionObserverOptions.threshold,
target
]);
React.useEffect(() => {
if (!target) {
return;
}
observer.observe(target);
return () => observer.unobserve(target);
}, [target, observer]);
return [isVisible, intersectionRatio];
};
type Visibility = boolean;
type IntersectionValue = number;
export interface UseIntersectionOptions {
root: Element | null;
rootMargin?: string;
threshold?: number;
}
export type UseIntersectionReturnValue = [Visibility, IntersectionValue];
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment