Как определить находится ли компонент в области видимости экрана?
Компоненты
Стандартный способ — IntersectionObserver: браузерный API, который асинхронно сообщает о пересечении элемента с областью просмотра, не нагружая главный поток.
Реализация хуком
const useInView = (options) => {
const ref = useRef(null);
const [inView, setInView] = useState(false);
useEffect(() => {
const node = ref.current;
if (!node) return;
const observer = new IntersectionObserver(
([entry]) => setInView(entry.isIntersecting),
options, // { root, rootMargin: '200px', threshold: 0.5 }
);
observer.observe(node);
return () => observer.disconnect(); // обязательный cleanup
}, [options]);
return [ref, inView];
};
// использование
const [ref, inView] = useInView({ rootMargin: '100px' });
return <section ref={ref}>{inView && <HeavyChart />}</section>;Опции: threshold — какая доля элемента считается видимой; rootMargin — «расширение» области (для предзагрузки до появления); root — скроллируемый контейнер вместо вьюпорта.
Где применяется
Ленивая загрузка изображений и тяжёлых виджетов, бесконечный скролл (сентинел внизу списка), аналитика показов, запуск анимаций и видео при появлении, sticky-подсветка активного пункта оглавления.
Почему не onScroll + getBoundingClientRect
Обработчик скролла срабатывает часто, а getBoundingClientRect() вызывает синхронный пересчёт layout — дёрганый интерфейс. IntersectionObserver считает пересечения сам браузер в цикле рендеринга, без принудительного reflow из JS, и вызывает колбэк асинхронно только при изменении пересечения.
Готовые решения
react-intersection-observer (useInView), встроенный loading="lazy" для изображений и iframe.
Коротко для собеседования
IntersectionObserver в useEffect с обязательным disconnect в cleanup: колбэк даёт isIntersecting, опции rootMargin/threshold управляют предзагрузкой и порогом. Применяется для lazy-загрузки, бесконечного скролла, аналитики показов. Вариант с onScroll + getBoundingClientRect хуже — частые срабатывания и синхронный reflow.