UNPKG

react-intersection-observer-hook

Version:
127 lines (123 loc) 3.85 kB
// src/use-intersection-observer.ts import { useCallback, useRef, useState } from "react"; // src/utils.ts function createObserverCache() { const cachesByRoot = /* @__PURE__ */ new Map(); function getObserver({ root, rootMargin, threshold }) { let cacheByRoot = cachesByRoot.get(root); if (!cacheByRoot) { cacheByRoot = /* @__PURE__ */ new Map(); cachesByRoot.set(root, cacheByRoot); } const cacheKey = JSON.stringify({ rootMargin, threshold }); let cachedObserver = cacheByRoot.get(cacheKey); if (!cachedObserver) { const entryCallbacks = /* @__PURE__ */ new Map(); const observer = new IntersectionObserver( (entries) => { entries.forEach((entry) => { const callback = entryCallbacks.get(entry.target); callback?.(entry); }); }, { root, rootMargin, threshold } ); cachedObserver = { observer, entryCallbacks }; cacheByRoot.set(cacheKey, cachedObserver); } return { observe: (node, callback) => { cachedObserver.entryCallbacks.set(node, callback); cachedObserver.observer.observe(node); }, unobserve: (node) => { cachedObserver.entryCallbacks.delete(node); cachedObserver.observer.unobserve(node); } }; } return { getObserver }; } // src/use-intersection-observer.ts var DEFAULT_ROOT_MARGIN = "0px"; var DEFAULT_THRESHOLD = [0]; var observerCache = createObserverCache(); function useIntersectionObserver(args) { const rootMargin = args?.rootMargin ?? DEFAULT_ROOT_MARGIN; const threshold = args?.threshold ?? DEFAULT_THRESHOLD; const nodeRef = useRef(null); const rootRef = useRef(null); const observerRef = useRef(null); const [entry, setEntry] = useState(); const reinitializeObserver = useCallback(() => { function cleanupObserver() { const observer = observerRef.current; const node = nodeRef.current; if (node) { observer?.unobserve(node); setEntry(void 0); } observerRef.current = null; } function initializeObserver() { const node = nodeRef.current; if (!node) return; const observer = observerCache.getObserver({ root: rootRef.current, rootMargin, threshold }); observer.observe(node, (observedEntry) => { setEntry(observedEntry); }); observerRef.current = observer; } cleanupObserver(); initializeObserver(); }, [rootMargin, threshold]); const refCallback = useCallback( (node) => { nodeRef.current = node; reinitializeObserver(); return () => { nodeRef.current = null; reinitializeObserver(); }; }, [reinitializeObserver] ); const rootRefCallback = useCallback( (rootNode) => { rootRef.current = rootNode; reinitializeObserver(); return () => { rootRef.current = null; reinitializeObserver(); }; }, [reinitializeObserver] ); return [refCallback, { entry, rootRef: rootRefCallback }]; } var use_intersection_observer_default = useIntersectionObserver; // src/use-track-visibility.ts import { useState as useState2 } from "react"; function useTrackVisibility(args) { const { once, ...rest } = args ?? {}; const [ref, result] = use_intersection_observer_default(rest); const isVisible = Boolean(result.entry?.isIntersecting); const [isVisibleOnce, setIsVisibleOnce] = useState2(isVisible); if (once && isVisible && !isVisibleOnce) { setIsVisibleOnce(true); } return [ref, { ...result, isVisible: once ? isVisibleOnce : isVisible }]; } var use_track_visibility_default = useTrackVisibility; export { use_intersection_observer_default as useIntersectionObserver, use_track_visibility_default as useTrackVisibility };