Intersection observer는 브라우저 뷰포트(Viewport)와 원하는 요소(Element)의 교차점을 관찰하며, 요소가 뷰포트에 포함되는지 아닌지 구별하는 기능을 제공한다. (더 쉽게는 특정 요소가 사용자 화면에 보이는지 안보이는지 판단함)
import { RefObject, useCallback, useEffect, useRef, useState } from 'react';
export const useInfiniteScroll = (targetElement: RefObject<HTMLDivElement>) => {
const observerRef = useRef<IntersectionObserver | null>(null);
const [intersecting, setIntersecting] = useState(false);
const getObserver = useCallback(() => {
if (!observerRef.current) {
observerRef.current = new IntersectionObserver(entries =>
setIntersecting(entries.some(entry => entry.isIntersecting))
);
}
return observerRef.current;
}, [observerRef.current]);
useEffect(() => {
if (targetElement.current) getObserver().observe(targetElement.current);
return () => getObserver().disconnect();
}, [targetElement.current]);
return intersecting;
};
동작 과정
div
태그 ref
를 인자로 받음const fetchMoreElement = useRef<HTMLDivElement>(null);
const intersecting = useInfiniteScroll(fetchMoreElement);
...
useEffect(() => {
if (intersecting && hasNext) {
fetcher('/test');
}
}, [intersecting]);
...
return (
<>
<Styled.Container ref={manRef}>
{cards.map((data: any, idx) => (
<Styled.Card key={data.description + idx}>
<Styled.Content>
<Styled.Title>{data.name + idx}</Styled.Title>
{data.description}
</Styled.Content>
</Styled.Card>
))}
</Styled.Container>
<div style={{ position: 'absolute' }} ref={fetchMoreElement}>
loading...
</div>
</>
);