import type { RefObject } from "react"; import { useState, useLayoutEffect } from "react"; import { getDocument } from "ssr-window"; import { getElRefHelper } from "./getElRefHelper"; export interface IUseIOProps { /** Функция, вызываемая при попадании элемента во viewPort */ callback: IntersectionObserverCallback; /** Референс элемента */ ref?: RefObject; /** Массив зависимостей */ deps?: unknown[]; /** Конфигурация наблюдателя */ options?: IntersectionObserverInit; } export interface IUseIOReturn { /** Экземпляр IntersectionObserver */ observer: IntersectionObserver | null; /** Функция для установки наблюдателя */ setObserver: (observer: IntersectionObserver | null) => void; } /** * Добавляет `IntersectionObserver` для компонента * @returns {IUseIOReturn} Возвращает созданный `IntersectionObserver` (или `null`) и функцию `setObserver` для ручного обновления экземпляра. * @example * import { useRef } from "react"; * import { useIO } from "@delement/ui/utils/react"; * * const App = () => { * * const ref = useRef(null); * const { observer, setObserver } = useIO({ ref, callback: (e) => console.debug(e) }); * * useEffect(() => { * if (observer) { * observer.observe(ref.current); * return () => observer.unobserve(ref.current); * } * }, [ observer ]); * * return
* } */ export const useIO = (props: IUseIOProps): IUseIOReturn => { const { callback, ref, deps = [], options = { threshold: 0, rootMargin: "0px", }, } = props || {}; const [ observer, setObserver ] = useState(null); useLayoutEffect(() => { const root = getElRefHelper(ref) || getDocument(); if (root && typeof callback === "function" && typeof IntersectionObserver !== "undefined") { const params: IntersectionObserverInit = { root: root as Element | Document | null, ...options, }; const IO = new IntersectionObserver(callback, params); setObserver(IO); return () => IO.disconnect(); } }, deps); return { observer, setObserver }; };