IntersectionObserver로 무한 스크롤 만들기

yo_onms·2022년 6월 8일
0

React

목록 보기
3/7
post-thumbnail

최근 무한 스크롤 구현을 하다가 scroll이벤트보다 좋은 방법으로 무한 스크롤 구현을 알아보다 Intersection Observer API를 알게되었고 제가 한번 해보도록 하겠습니다!

1. 설치

 yarn add react-intersection-observer

또는

 npm i react-intersection-observer

2. 설명

Intersection Observer 예제

 let observer = new IntersectionObserver(callback, options);

Intersection Observer를 생성할 때는 3가지 옵션 설정이 가능하다.
옵션에는 root, rootMargin, threshold 이 있다.

  • root : 이 요소를 기준으로 관찰하고자하는 요소의 가시성을 결정한다. (default: 브라우저 viewport)
  • rootMargin: 단어의 뜻 그대로 root가 갖는 margin 값이다. threshold를 계산할 때 rootMargin 만큼 더 계산합니다. (default: 0)
  • threshold : root를 기준으로 대상요소가 얼마만큼 보일때 콜백함수를 실행할지 0~1 사이의 값으로 나타낸다. 만약 요소가 25%씩 보일때마다 실행하고 싶다면 [0, 0.25, 0.5, 0.75, 1] 와 같이 배열로 작성하면 된다. (default: 1)

3. 구현

우선 Style 부분의 설명은 제외하고 설명을 하겠습니다.

전체코드

const InfiniteScroll = () => {
  const [itemList, setItemList] = useState<number[]>([
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
  ]);
  const [target, setTarget] = useState<HTMLElement | null | undefined>(null);
  const [isLoading, setIsLoading] = useState<boolean>(false);

  const onIntersect: IntersectionObserverCallback = async (
    [entry],
    observer,
  ) => {
    if (entry.isIntersecting && !isLoading) {
      //관찰대상 끊기
      observer.unobserve(entry.target);
      //로딩컴포넌트 on
      setIsLoading(true);
      // 데이터를 가져오는 부분
      await new Promise((resolve) => setTimeout(resolve, 1000));
      let Items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
      setItemList((itemLists) => itemLists.concat(Items));
      setIsLoading(false);
      //새로운 관찰대상 target
      observer.observe(entry.target);
    }
  };
 return (
    <>
      <ItemWrap>
        {itemList.map((item: number, index: number) => {
          return <Styled.Item>{index + 1}</Styled.Item>;
        })}
      </ItemWrap>
      {isLoading ? (
        <LoaderWrap>
          <ReactLoading type="spin" color="#A593E0" />
        </LoaderWrap>
      ) : (
        ''
      )}
      <div ref={setTarget} />
    </>
  );
};
  
  useEffect(() => {
    if (!target) return;

    let observer: IntersectionObserver;

    if (target) {
      observer = new IntersectionObserver(onIntersect, {
        threshold: 0.4,//margin이라고 생각하면 편합니다.
      });
      observer.observe(target);
    }

    return () => observer && observer.disconnect();
  }, [target]);

  return (
    <>
      <ItemWrap>
        //이미지 렌더링
        {itemList.map((item: number, index: number) => {
          return <Item>{index + 1}</Item>;
        })}
      </ItemWrap>
	  //로딩시 보이는 컴포넌트
      {isLoading ? (
        <LoaderWrap>
          <ReactLoading type="spin" color="#A593E0" />
        </LoaderWrap>
      ) : (
        ''
      )}
	  //감시하는곳
      <div ref={setTarget} />
    </>
  );
};

설명

  • ref를 통해 대상요소를 observe 한다.
  • useEffect를 통해 마지막 대상을 관찰하다 마지막 부분에 닿으면 통신을 통해 새로운 데이터 붙이기 + 기존 관찰 대상 -> 마지막 데이터로 관찰대상 변경

4. 느낀점

생각보다 개념이 어렵게 느껴져서 애먹은거 같은데 막상 구현하고 보니 이해가 되었던거 같다. 이런식으로 웹 성능과 관련된 것들을 더 공부해 나아 가야 겠다.

참조

profile
프론트엔드 주니어 개발자

0개의 댓글