Skip to content

Instantly share code, notes, and snippets.

@tronghieu60s
Created August 31, 2022 06:24
Show Gist options
  • Save tronghieu60s/940491d37404dae83c4b4d214269c01d to your computer and use it in GitHub Desktop.
Save tronghieu60s/940491d37404dae83c4b4d214269c01d to your computer and use it in GitHub Desktop.
import React, { useCallback, useEffect, useRef, useState } from 'react';
type IOProps = {
ref: React.RefObject<any>;
};
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<HTMLDivElement>(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 <div ref={containerRef} />;
}
@tronghieu60s
Copy link
Author

Using:

<InView>
  {({ ref }) => (
    <div ref={ref}>
      <h2>Review</h2>
    </div>
  )}
</InView>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment