Intersection Observer의 필요성
- 페이지 스크롤 시 이미지를 Lazy-loading(지연 로딩)할 때
- Infinite scrolling(무한 스크롤)을 통해 스크롤할 때 새로운 콘텐츠를 불러올 때
- 광고의 수익을 계산하기 위해 광고의 가시성을 참고할 때
- 사용자가 결과를 볼 것인지에 따라 애니메이션 동작 여부를 결정할 때
=>브라우저 뷰포트(Viewport)와 설정한 요소(Element)의 교차점을 관찰하며, 요소가 뷰포트에 포함되는지 포함되지 않는지, 더 쉽게는 사용자 화면에 지금 보이는 요소인지 아닌지를 구별하는 기능을 제공한다
scroll 이벤트와 다르게 교차 시 비동기적으로 실행되며 가시성 구분 시 reflow 를 발생하지 않는다
Intersection Observer 사용 방법
// 기본구조는 콜백함수와 옵션을 받는다.
const io = new IntersectionObserver(callback[, options])
new 키워드를 통해 인스턴스를 생성하고 . callback , options 2개의 파라미터를 받는다
Parameters
callback
Entries
entries 는 IntersectionObserverEntry 인스턴스를 담은 배열이다. 일반적으로 callback 에 파라미터로 전달이 되고 Intersection Observer.takeRecords() 를 통해 반환받을 수도 있다.
IntersectionObserverEntry 는 루트요소와 타겟요소의 교차(threshold 와 만났을 때)의 상황을 묘사합니다. 포함된 프로퍼티들은 모두 읽기전용(read only) 이다
- IntersectionObserverEntry.boundingClientRect : 타겟 요소의 사각형 정보(DOMRectReadOnly)를 반환한다. getBoundingClientRect() 호출과는 다르게 reflow 를 발생시키진 않는다.
- IntersectionObserverEntry.intersectionRect : 타겟 요소의 가시성이 감지된 부분의 정보(DOMRectReadOnly)를 반환한다.
- IntersectionObserverEntry.intersectionRatio : 타겟 요소의 intersectionRect 이 boundingClientRect 와 어느정도로 교차(겹치는 지) 비율(0.0 ~ 1.0)을 반환한다 ( 타겟 요소가 루트 요소와 얼마나 교차하는지의 정도와 같다)
Methods
- IntersectionObserver.observe(targetElement) : 타겟 요소에 대한 관찰을 시작
- IntersectionObserver.unobserve(targetElement) : 타겟 요소에 대한 관찰을 중지. 관찰의 목적이 이루어져 굳이 계속 관찰을 할 필요가 없는 경우 사용.
- IntersectionObserver.disconnect() : 인스턴스의 타겟 요소들에 대한 모든 관찰을 중지.
- IntersectionObserver.takerecords(targetElement) : IntersectionObserverEntry 인스턴스들의 배열을 리턴.
options
root
타겟 요소의 가시성을 확인할 때 사용되는 루트 요소입니다. 이것은 타겟 요소보다 상위 요소,요소의 조상 요소여야 한다
설정하지 않거나 root 값을 null 로 주었을 때 기본 값으로 브라우저 뷰포트가 설정됩니다.
rootMargin
margin 을 주어 루트 요소의 범위를 확장할 수 있다. 즉 확장된 영역 안에 타겟 요소가 들어가면 가시성에 변화가 생긴다 CSS 의 margin 과 유사하게 top, right, bottom, left 의 margin 정도롤 각각 설정할 수 있다. 기본 값은 0이며 따로 설정 시 단위를 꼭 입력해야한다
threshold
콜백이 실행될 타겟 요소의 가시성 퍼센티지를 나타내는 단일 숫자 및 숫자 배열이 들어갈 수 있다. 요소의 top, bottom 이 노출된 순간만 콜백을 실행할 수 있는 것이 아니라 어느정도 타겟 요소가 보여졌는 지에 따라서도 콜백을 호출할 수 있다. 예를 들어 요소가 50%만큼 보여졌을 때 탐지하고 싶다면 단일 숫자 값 0.5 를 설정하면 된다. 혹은 25% 단위로 가시성이 변경될 때마다 콜백이 실행되게 하고 싶다면 [0, 0.25, 0.5, 0.75, 1] 을 설정하면 된다.
🥲무한 스크롤 구현하기
아직 완벽한 이해를 못했지만... 다시 코드를 뜯어 볼것🙂
데이터 패칭하기
- fetch함수를 이용해서 데이터를 불러오는 함수 를 만들어준다. 무한 스크롤을 구현하기 위해서는 데이터를 처음부터 계속 보여줘야하기 때문에 데이터를 담고 있는 state인 movie 를 operator연산자로 복제한다
- 스크롤이 닿았을 때 새롭게 데이터 페이지를 바꾸는 state인 page를 만들어준다. 그리고 useEffect로 page의 넘버가 바뀔 때마다 movieHandler함수를 호출시킨다.
const [movie, setMovie] = useState([]);
const [page, setPage] = useState(1); //스크롤이 닿았을 때 새롭게 데이터 페이지를 바꿀 state
const [loading, setLoading] = useState(false); //로딩 성공, 실패를 담을 state
const movieHandler = (page) => {
axios
.get(`https://yts.mx/api/v2/list_movies.json?page=${page}&limit=3`)
.then((res) => {
setMovie((pre) => [...pre, ...res.data.data.movies]);
setLoading(true);
});
};
page넘버를 바꾸는 함수 만들기
- movieHandler를 실행시켜 데이터를 불러오고 page를 1씩 증가시켜서 그 다음 데이터를 불러오도록 한다. 새롭게 불러와진 데이터는 movieHandler를 통해서 기존에 있던 데이터에 더해지게 된다.
- loadMore함수를 만들어서 page가 1씩 더해지는 함수를 만든다
useEffect(() => {
movieHandler(page);
}, [page]);
useRef를 사용한 target설정하기
무한 스크롤은 마지막 엘리먼트에 닿았을 때 데이터를 자동으로 불러온다. 이 페이지의 마지막 요소는 <div><div/> 이기 때문에 여기에 ref를 지정해놓고 탐색 타겟으로 설정한다.
=> <div></div>에 도달했을 때 데이터 패칭이 된다
return (
<ul className={classes["movies-list"]}>
{props?.movies.map((movie) => (
<Movie
key={v4()}
title={movie?.title}
releaseDate={movie?.openDt}
openingText={movie?.summary}
rating={movie.rating}
/>
))}
<div ref={pageEnd}></div>
</ul>
);
};
Intersection Observer를 사용해서 뷰포트 안에 있는 요소 확인하기
- Intersection Observer를 통해서 뷰포트 내에 지정해둔 타겟 div를 찾고 있으면, loadMore함수를 실행시켜 page를 1씩 증가 시킨다
const MovieList = (props) => {
const pageEnd = useRef();
useEffect(() => {
if (props.loading) {
//로딩되었을 때만 실행
const observer = new IntersectionObserver(
(entries) => {
if (entries[0].isIntersecting) {
props.handle();
}
},
{ threshold: 1 }
);
//옵져버 탐색 시작
observer.observe(pageEnd.current);
}
}, [props.loading]);
'React' 카테고리의 다른 글
페이지네이션 커서기반과 오프셋기반 (0) | 2023.01.16 |
---|---|
useEffect 와 useLayoutEffect (0) | 2023.01.15 |
redux persist (0) | 2022.12.19 |
redux-toolkit (0) | 2022.12.19 |
Fragment/Portal (0) | 2022.12.04 |