import React, { useCallback, useEffect, useRef, useState } from 'react'; type IOProps = { ref: React.RefObject; }; type Props = { skeleton?: React.ReactNode; skeletonDuration?: number; options?: { root: IntersectionObserver['root']; rootMargin: IntersectionObserver['rootMargin']; thresholds: IntersectionObserver['thresholds']; }; continueObserving?: boolean; onIntersection?: (entries: IntersectionObserverEntry[]) => void; children: (fields: IOProps) => React.ReactNode; }; const defaultOptions = { root: null, rootMargin: '0px', thresholds: 0, }; export default function IntersectionObserverInView(props: Props): React.ReactElement { const { skeleton, skeletonDuration = 3000, options = defaultOptions, continueObserving, onIntersection, children, } = props; const containerRef = useRef(null); const [isShow, setIsShow] = useState(false); const [hasIntersected, setHasIntersected] = useState(false); const onIntersectionCallback = useCallback( (entries: IntersectionObserverEntry[], observer: IntersectionObserver) => { if (!continueObserving && !hasIntersected) { const entry = entries.find((e) => e.target === containerRef.current); if (entry && entry.isIntersecting) { setHasIntersected(true); setTimeout(() => setIsShow(true), skeleton ? skeletonDuration : 0); if (onIntersection) onIntersection(entries); if (containerRef.current) observer.unobserve(containerRef.current); } } else if (continueObserving && onIntersection) { onIntersection(entries); } }, [continueObserving, hasIntersected, onIntersection, skeleton, skeletonDuration], ); useEffect(() => { let observerRefValue: Element | null = null; const observer = new IntersectionObserver(onIntersectionCallback, options); if (containerRef.current) { observer.observe(containerRef.current); observerRefValue = containerRef.current; } return () => { if (observerRefValue) observer.unobserve(observerRefValue); observer.disconnect(); }; }, [hasIntersected, containerRef, onIntersectionCallback, options]); const childrenWithProps = children({ ref: containerRef }) as React.ReactElement; if (continueObserving) { return childrenWithProps; } if (isShow && hasIntersected) { return childrenWithProps; } if (hasIntersected) { return skeleton as React.ReactElement; } return
; }