IntersectionObserver API 를 이용해서 component가 view 에 잡히는지 확인
intersectionObserver Api 를 hook 으로 처리하기 위해 아래처럼 코드 작성. (null type check 때문에 코드가 조잡해지긴 했다.)
import { RefObject, useEffect, useRef, useState } from 'react';
export function useOnScreen(ref: RefObject<HTMLElement>) {
const observerRef = useRef<IntersectionObserver | null>(null);
const [isOnScreen, setIsOnScreen] = useState(false);
useEffect(() => {
observerRef.current = new IntersectionObserver(([entry]) =>
setIsOnScreen(entry.isIntersecting),
);
}, []);
useEffect(() => {
if (observerRef.current === null || ref.current === null) {
return;
}
observerRef.current.observe(ref.current);
return () => {
if (observerRef.current === null) {
return;
}
observerRef.current.disconnect();
};
}, [ref]);
return isOnScreen;
}
무한 스크롤을 구현하고 싶은 컴포넌트 마지막 줄에 감지할 컴포넌트를 넣고 ref 로 isOnScreen 을 넣을 ref 를 넣어준다
(더이상 받아올 컨텐츠가 없으면 ref null 처리)
<LastLine ref={curPage.current === -1 ? null : elementRef}></LastLine>
서버에 요청 보내기 전&후 처리 코드
//무한 페이지네이팅에 필요한 훅들
const curPage = useRef<number>(0);
const elementRef = useRef<HTMLDivElement>(null);
const isOnScreen = useOnScreen(elementRef);
const [isRequesting, setIsRequesting] = useState<boolean>(false);
//뷰에 들어옴이 감지될 때 요청 보내기
const getNewsContent = useCallback(async () => {
setIsRequesting(true);
try {
const Previews: Array<Preview> = await NewsService.getPreviews(curPage.current);
if (Previews.length === 0) {
curPage.current = -1;
return;
}
if (curPage.current === 0) {
setPreviewsDefault(Previews);
}
curPage.current += 1;
const newPreviews = curPreviews.concat(Previews);
setCurPreviews(newPreviews);
return 0;
} catch (e) {
console.error(e);
} finally {
setIsRequesting(false);
}
}, [curPreviews]);
useEffect(() => {
//요청 중이라면 보내지 않기
if (isOnScreen === true && isRequesting === false) {
getNewsContent();
} else {
return;
}
}, [isOnScreen]);
아직 버그난 건 없으나 날 때마다 수정함.