좀 더 자세하게 만들고 커스텀 훅으로 발전시켜 보기 위해
참고자료 2 - velog
를 더 참고해 보았다.
Intersection Observer API를 사용해 화면에 띄어준 콘텐츠 중에 맨 마지막 요소를 관측해 콜백함수를 실행하는 원리 -> 무한스크롤 구현
타겟이 관측 되었다면 실행되는 콜백 함수 내에서 해당 요소에 대한 관측을 중지하고, 새로운 콘텐츠를 리스트에 추가, 이후 다시 마지막 콘텐츠에서 관측!
1.관찰자 생성
const defaultOption = {
root: null,
threshold: 0.5,
rootMargin: '0px'
};
const observer = new IntersectionObserver(checkIntersect, {
...defaultOption,
...option
}
callback함수 checkIntersect
는 타겟을 주시하는 역할,
두번째 파라미터는 옵션으로 defaultOption
을 교차공간에 대한 프로퍼티로 전달한다.
2.관찰대상 정해주기
이전에 했던 것과 같이 요소에 ref를 통해 지정해준다.
3.관찰자 만들기
const checkIntersect = useCallback(([entry], observer) => {
if (entry.isIntersecting) {
onIntersect(entry, observer);
}
}, []);
관찰 대상은 하나이므로 콜백함수의 인자로 들어오는 [entry]
<- 하나만 받기 때문에 구조분해 할당으로 첫번째요소를 받는 것
그리고 observer
를 파라미터로 전달해준다.
이후 entry 속성인 isIntersecting
을 이용해 조건을 검사하고 조건에 해당하면 콜백함수 실행!
※하지만 관찰자, 관찰 대상, 조건, 콜백 함수를 만들었지만 이대로 실행한다면 원하는대로 나오지 않는다.
관찰 대상은 새로운 데이터를 가져올때마다 변해야 하기 때문!
!우리에겐 useEffect가 있다
1. 스크롤을 내린다.
2. 관찰대상을 만나고, 조건을 만족시킨다.
3. 새로운 데이터를 가져오고 파라미터로 함께 boolean값을 넘겨줘 관찰 대상이 사라지도록 만든다.
4. 새로운 데이터를 리스트에 추가하고 loading이 다시 false가 되었기 때문에 관찰 대상이 다시 렌더링 된다.
5. 관찰 대상은 다시 랜더링 되었으나, 새롭게 만든다. 지워진 관찰 대상은 리스트에서 제거했고, 새롭게 관찰대상을 지정한다.
useEffect(() => {
let observer; // (1)beserver 변수를 선언해주고
if (ref) { // (2) 관찰대상이 존재하는지 체크한다.
observer = new IntersectionObserver(checkIntersect, {
...defaultOption,
...option
}); // (3) 관찰대상이 존재한다면 관찰자를 생성한다.
observer.observe(ref); // (4) 관찰자에게 타겟을 지정한다.
}
return () => observer && observer.disconnect(); // 의존성에 포함된 값이 바뀔때 관찰을 중지한다.
}, [ref, option, checkIntersect]);
그렇다면 커스텀 훅으로 만들어보자
Intersecction Observer는 무한 스크롤만을 위한 기능이 아닌 걸 저번에 설명했다.
이연 로딩이나 스켈레톤 UI를 구현할 때도 사용되기 때문!
커스텀 훅으로 만들어보자
import { useState, useEffect, useCallback } from 'react';
// 옵션 값을 지정한다.
const defaultOption = {
root: null,
threshold: 0.5,
rootMargin: '0px'
};
// 커스텀 훅 부분
// 관찰 대상을 지정할 수 있도록 ref값을 useState 훅을 이용해 state로 관리해준다.
// 관찰자를 만들어준다.
const useIntersect = (onIntersect, option) => {
const [ref, setRef] = useState(null);
const checkIntersect = useCallback(([entry], observer) => {
if (entry.isIntersecting) {
onIntersect(entry, observer);
}
}, []);
// 관찰자가 언제 관찰하는지, 관찰을 종료하는지에 대해 로직을 구현해준다.
useEffect(() => {
let observer;
if (ref) {
observer = new IntersectionObserver(checkIntersect, {
...defaultOption,
...option
});
observer.observe(ref);
}
return () => observer && observer.disconnect();
}, [ref, option.root, option.threshold, option.rootMargin, checkIntersect]);
return [ref, setRef];
}
export default useIntersect;