signal-desktop/ts/hooks/useIntersectionObserver.ts

63 lines
1.6 KiB
TypeScript
Raw Normal View History

2023-01-03 19:55:46 +00:00
// Copyright 2020 Signal Messenger, LLC
2021-09-17 22:24:21 +00:00
// SPDX-License-Identifier: AGPL-3.0-only
import { useCallback, useRef, useState } from 'react';
import * as log from '../logging/log';
/**
* A light hook wrapper around `IntersectionObserver`.
*
* Example usage:
*
* function MyComponent() {
* const [intersectionRef, intersectionEntry] = useIntersectionObserver();
* const isVisible = intersectionEntry
* ? intersectionEntry.isIntersecting
* : true;
*
* return (
* <div ref={intersectionRef}>
* I am {isVisible ? 'on the screen' : 'invisible'}
* </div>
* );
* }
*/
export function useIntersectionObserver(): [
(el?: Element | null) => void,
IntersectionObserverEntry | null
] {
2021-11-11 22:43:05 +00:00
const [intersectionObserverEntry, setIntersectionObserverEntry] =
useState<IntersectionObserverEntry | null>(null);
2021-09-17 22:24:21 +00:00
const unobserveRef = useRef<(() => unknown) | null>(null);
const setRef = useCallback((el?: Element | null) => {
if (unobserveRef.current) {
unobserveRef.current();
unobserveRef.current = null;
}
if (!el) {
return;
}
const observer = new IntersectionObserver(entries => {
if (entries.length !== 1) {
log.error(
'IntersectionObserverWrapper was observing the wrong number of elements'
);
return;
}
entries.forEach(entry => {
setIntersectionObserverEntry(entry);
});
});
unobserveRef.current = observer.unobserve.bind(observer, el);
observer.observe(el);
}, []);
return [setRef, intersectionObserverEntry];
}