
무한스크롤을 만들때 주로사용하는 IntersectionObserver를 예시와 함께 알아봅니다.


전체 코드는 github를 참고해주세요.
const CardList: FC<{ data: Docs[]; fetchMore: () => void }> = ({
data,
fetchMore,
}) => {
const { lastItemRef } = useInfiniteScroll(fetchMore);
return (
<StyledCardList>
{data?.map((book, index) => {
if (index === data.length - 1) {
return (
<div ref={lastItemRef} key={index}>
<CardItem book={book} />
</div>
);
}
return <CardItem book={book} key={index} />;
})}
</StyledCardList>
);
};
import { useCallback, useRef } from 'react';
function useInfiniteScroll(fetchMore: () => any) {
const observerRef = useRef<IntersectionObserver | null>(null);
const lastItemRef = useCallback((node: HTMLDivElement) => {
if (observerRef.current) {
observerRef.current.disconnect();
}
observerRef.current = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting) {
// fetch를 하기위한 callback
fetchMore();
}
}, { rootMargin: '200% 0px' });
if (node) {
observerRef.current.observe(node);
}
}, []);
return {
lastItemRef,
};
}
export default useInfiniteScroll;
IntersectionObserver를 넣어둘 useRef를 생성합니다.
여기서의 useRef는 매번 새롭게 생성하는 IntersectionObserver를 담아둘 공간으로, 일반적으로 DOM을 참조하기 위한 목적이 아닙니다.
CardList 컴포넌트에서 마지막 CardItem에 ref에 lastItemRef를 넣어줍니다.
ref 함수 작성
<div ref={lastItemRef} key={index}>는 <div ref={(ref) => lastItemRef(ref)} key={index}>와 같습니다. 즉 node element를 받게 됩니다.기존 lastItem의 observe 중단
observerRef.current.disconnect();로 모든관찰을 중단합니다.새로운 IntersectionObserver 생성
observerRef.current = new IntersectionObserver으로 새로운 IntersectionObserver를 생성합니다. IntersectionObserver의 parameter는 두가지가 있습니다. (자세한 설명은 이 블로그를 참고해주세요.) IntersectionObserver의 첫번째 parameter

IntersectionObserver의 두번째 parameter
CardList를 생성할때 lastItemRef로 마지막 Item을 관찰합니다.
ref를 생성하면서 기존관찰을 모두 disconnect()하고, 내부에 new IntersectionObserver()를 생성합니다. 그리고 node를 관찰(observe(node))합니다.
관찰하던 node와 root의 교차(intersection)가 발생하면, IntersectionObserver의 callback이 실행됩니다.
callback함수에 의해 fetchMore이 실행됩니다.