UNPKG

react-in-viewport

Version:

Track React component in viewport using Intersection Observer API

159 lines (153 loc) 5.47 kB
(function (global, factory) { if (typeof define === "function" && define.amd) { define(["exports", "react", "./constants"], factory); } else if (typeof exports !== "undefined") { factory(exports, require("react"), require("./constants")); } else { var mod = { exports: {} }; factory(mod.exports, global.react, global.constants); global.useInViewport = mod.exports; } })(typeof globalThis !== "undefined" ? globalThis : typeof self !== "undefined" ? self : this, function (_exports, _react, _constants) { "use strict"; _exports.__esModule = true; _exports["default"] = void 0; 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 _props = props, onEnterViewport = _props.onEnterViewport, onLeaveViewport = _props.onLeaveViewport; var _useState = (0, _react.useState)(), forceUpdate = _useState[1]; 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 _useState2 = (0, _react.useState)(Boolean(target.current)), isTargetReady = _useState2[0], setIsTargetReady = _useState2[1]; function startObserver(_ref) { var observerRef = _ref.observerRef; var targetRef = target.current; if (targetRef) { var node = targetRef; if (node) { observerRef == null ? void 0 : observerRef.observe(node); } } } function stopObserver(_ref2) { var observerRef = _ref2.observerRef; 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 = function handleIntersection(entries) { var entry = entries[0] || {}; var isIntersecting = entry.isIntersecting, intersectionRatio = entry.intersectionRatio; 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.observerRef; if (!observerRef) { observer.current = new IntersectionObserver(handleIntersection, options); return observer.current; } return observerRef; } (0, _react.useEffect)(function () { var observerRef = observer.current; // https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API observerRef = initIntersectionObserver({ observerRef: observerRef }); startObserver({ observerRef: observerRef }); return function () { stopObserver({ observerRef: observerRef }); }; }, [target.current, options, config, onEnterViewport, onLeaveViewport]); // Use MutationObserver to detect when `target.current` becomes non-null // only at start up (0, _react.useEffect)(function () { var currentElement = target.current; var mutationObserver = null; // MutationObserver callback to check when the target ref is assigned var handleOnChange = function 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 function () { if (mutationObserver) { mutationObserver.disconnect(); } }; }, [target.current]); return { inViewport: inViewportRef.current, enterCount: enterCountRef.current, leaveCount: leaveCountRef.current }; }; var _default = _exports["default"] = useInViewport; });