Intersection Observer API

sanha_OvO·2022년 6월 24일
0

JavaScript

목록 보기
8/8

🤔 Intersection Observer?

타겟 요소와 상위 요소 또는 최상위 document의 뷰 포트 사이의 관찰 범위의 변화를 비동기적으로 관찰하는 방법입니다. - MDN Web Docs

인터섹션 정보의 사용

  • 페이지가 스크롤 되는 동중에 발생하는 이미지나 다른 컨텐츠의 지연 로딩
  • 스크롤 시 더 많은 콘텐츠의 렌더링을 위한 화면 범위 감지
  • 광고 컨테이너의 가시성 체크 (광고 수익성에 연관)
  • 사용자에게 표시되는 화면에 따라 작업이나 애니메이션 수행

과거 인터섹션에 따른 이벤트 처리를 위해 스크롤 이벤트나 Element.getBoundingClientRect()와 같은 메서드를 이용해야 했음 → 이벤트들이 매우 복잡하게 얽히기 때문에 유지/보수 및 성능 문제에 직면하게됨

😎 Intersection Observer API

타겟이 되는 요소가 뷰포트 혹은 다른 요소에 특정 부분만큼 들어가거나 나갈 때 콜백으로 걸어둔 로직을 처리하도록 할 수 있게된다.


🧑‍💻 Intersection Observer API를 이용한 무한 스크롤 구현

useIntersection.tsx

import { useRef, useEffect, useState } from 'react';
import { useRecoilState } from 'recoil';
import { searchResultState } from 'src/recoils/SearchRecoil';
import { fetchGetData } from 'src/utils/fetchData';

export const useIntersection = () => {
  const [infiniteScrollTarget, setInfiniteScrollTarget] = useState<HTMLElement | null | undefined>(null);
  const observerRef = useRef<IntersectionObserver>();
  const [{ keyword, totalResults, page, search }, setSearchResult] = useRecoilState(searchResultState);

  const onIntersect = async ([entry]: IntersectionObserverEntry[], observer: IntersectionObserver) => {
    if (entry.isIntersecting && page * 10 < totalResults) {
      observer.unobserve(entry.target);

      const data = await fetchGetData(keyword, page + 1);

      setSearchResult((prev) => ({
        keyword: prev.keyword,
        search: [...prev.search, ...data.Search],
        response: Boolean(data.Response),
        totalResults: Number(data.totalResults),
        page: prev.page + 1,
      }));
    }
  };

  useEffect(() => {
    if (infiniteScrollTarget) {
      observerRef.current = new IntersectionObserver(onIntersect, {
        threshold: 0.6,
      });
      observerRef.current.observe(infiniteScrollTarget);
    }
    return () => observerRef.current && observerRef.current.disconnect();
  }, [search, infiniteScrollTarget]);

  return { setInfiniteScrollTarget };
};

Page.tsx

import React from 'react';
import { useRecoilValue } from 'recoil';
import styled from 'styled-components';
import { searchResultState } from 'src/recoils/SearchRecoil';
import SearchBar from 'src/components/SearchBar';
import MovieElement from 'src/components/MovieElement';
import { useIntersection } from 'src/hooks/useIntersection';

function Search(): JSX.Element {
  const { search } = useRecoilValue(searchResultState);
  const { setInfiniteScrollTarget } = useIntersection();

  return (
    <>
      <SearchBar />
      <AppMain>
        {!search.length && <NoResult>검색결과가 없습니다.</NoResult>}
        {search?.map((info) => (
          <MovieElement info={info} key={info.imdbID} />
        ))}
        <ScrollTarget ref={setInfiniteScrollTarget} />
      </AppMain>
    </>
  );
}

export default Search;

const AppMain = styled.main`
  padding: 0 7vw;
  margin-bottom: 8vh;
`;

const NoResult = styled.p`
  margin-top: 30px;
  font-size: 30px;
  text-align: center;
`;

const ScrollTarget = styled.div`
  height: 150px;
`;

인용

Intersection Observer API - Web API | MDN

profile
Web Developer / Composer

0개의 댓글