import React, { useState, useRef, useEffect, ReactNode, MutableRefObject } from "react"; import { cn } from "helpers/common.helper"; type Props = { defaultHeight?: string; verticalOffset?: number; horizontalOffset?: number; root?: MutableRefObject; children: ReactNode; as?: keyof JSX.IntrinsicElements; classNames?: string; alwaysRender?: boolean; placeholderChildren?: ReactNode; pauseHeightUpdateWhileRendering?: boolean; changingReference?: any; }; const RenderIfVisible: React.FC = (props) => { const { defaultHeight = "300px", root, verticalOffset = 50, horizontalOffset = 0, as = "div", children, classNames = "", alwaysRender = false, //render the children even if it is not visible in root placeholderChildren = null, //placeholder children pauseHeightUpdateWhileRendering = false, //while this is true the height of the blocks are maintained changingReference, //This is to force render when this reference is changed } = props; const [shouldVisible, setShouldVisible] = useState(alwaysRender); const placeholderHeight = useRef(defaultHeight); const intersectionRef = useRef(null); const isVisible = alwaysRender || shouldVisible; // Set visibility with intersection observer useEffect(() => { if (intersectionRef.current) { const observer = new IntersectionObserver( (entries) => { //DO no remove comments for future // if (typeof window !== undefined && window.requestIdleCallback) { // window.requestIdleCallback(() => setShouldVisible(entries[0].isIntersecting), { // timeout: 300, // }); // } else { // setShouldVisible(entries[0].isIntersecting); // } setShouldVisible(entries[entries.length - 1].isIntersecting); }, { root: root?.current, rootMargin: `${verticalOffset}% ${horizontalOffset}% ${verticalOffset}% ${horizontalOffset}%`, } ); observer.observe(intersectionRef.current); return () => { if (intersectionRef.current) { // eslint-disable-next-line react-hooks/exhaustive-deps observer.unobserve(intersectionRef.current); } }; } }, [intersectionRef, children, root, verticalOffset, horizontalOffset]); //Set height after render useEffect(() => { if (intersectionRef.current && isVisible) { placeholderHeight.current = `${intersectionRef.current.offsetHeight}px`; } }, [isVisible, intersectionRef, alwaysRender, pauseHeightUpdateWhileRendering]); const child = isVisible ? <>{children} : placeholderChildren; const style = isVisible && !pauseHeightUpdateWhileRendering ? {} : { height: placeholderHeight.current, width: "100%" }; const className = isVisible ? classNames : cn(classNames, "bg-custom-background-80"); return React.createElement(as, { ref: intersectionRef, style, className }, child); }; export default RenderIfVisible;