UNPKG

react-in-viewport

Version:

Track React component in viewport using Intersection Observer API

153 lines (147 loc) 4.46 kB
"use strict"; exports.__esModule = true; exports.default = void 0; var _react = require("react"); var _constants = require("./constants"); var defaultMutationObserverOption = { attributes: true, childList: true, subtree: true }; var useInViewport = function useInViewport(target, options, config, props) { if (options === void 0) { options = _constants.defaultOptions; } if (config === void 0) { config = _constants.defaultConfig; } if (props === void 0) { props = _constants.defaultProps; } var { onEnterViewport, onLeaveViewport } = props; var [, forceUpdate] = (0, _react.useState)(); var observer = (0, _react.useRef)(); var inViewportRef = (0, _react.useRef)(false); var intersected = (0, _react.useRef)(false); var enterCountRef = (0, _react.useRef)(0); var leaveCountRef = (0, _react.useRef)(0); // State to track when target is available var [isTargetReady, setIsTargetReady] = (0, _react.useState)(Boolean(target.current)); function startObserver(_ref) { var { observerRef } = _ref; var targetRef = target.current; if (targetRef) { var node = targetRef; if (node) { observerRef == null ? void 0 : observerRef.observe(node); } } } function stopObserver(_ref2) { var { observerRef } = _ref2; var targetRef = target.current; if (targetRef) { var node = targetRef; if (node) { observerRef == null ? void 0 : observerRef.unobserve(node); } } observerRef == null ? void 0 : observerRef.disconnect(); observer.current = null; } var handleIntersection = entries => { var entry = entries[0] || {}; var { isIntersecting, intersectionRatio } = entry; var isInViewport = typeof isIntersecting !== 'undefined' ? isIntersecting : intersectionRatio > 0; // enter if (!intersected.current && isInViewport) { intersected.current = true; onEnterViewport == null ? void 0 : onEnterViewport(); enterCountRef.current += 1; inViewportRef.current = isInViewport; forceUpdate(isInViewport); return; } // leave if (intersected.current && !isInViewport) { intersected.current = false; onLeaveViewport == null ? void 0 : onLeaveViewport(); if (config.disconnectOnLeave && observer.current) { // disconnect observer on leave observer.current.disconnect(); } leaveCountRef.current += 1; inViewportRef.current = isInViewport; forceUpdate(isInViewport); } }; function initIntersectionObserver(_ref3) { var { observerRef } = _ref3; if (!observerRef) { observer.current = new IntersectionObserver(handleIntersection, options); return observer.current; } return observerRef; } (0, _react.useEffect)(() => { var observerRef = observer.current; // https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API observerRef = initIntersectionObserver({ observerRef }); startObserver({ observerRef }); return () => { stopObserver({ observerRef }); }; }, [target.current, options, config, onEnterViewport, onLeaveViewport]); // Use MutationObserver to detect when `target.current` becomes non-null // only at start up (0, _react.useEffect)(() => { var currentElement = target.current; var mutationObserver = null; // MutationObserver callback to check when the target ref is assigned var handleOnChange = () => { if (target.current && !isTargetReady) { setIsTargetReady(true); if (mutationObserver) { mutationObserver.disconnect(); } } }; if (currentElement) { setIsTargetReady(true); // If target is already available, mark it ready } else { // Observe changes to detect when `target.current` becomes non-null mutationObserver = new MutationObserver(handleOnChange); mutationObserver.observe(document.body, defaultMutationObserverOption); } // Cleanup function to stop observing when the component unmounts return () => { if (mutationObserver) { mutationObserver.disconnect(); } }; }, [target.current]); return { inViewport: inViewportRef.current, enterCount: enterCountRef.current, leaveCount: leaveCountRef.current }; }; var _default = exports.default = useInViewport;