본문 바로가기
TIL

observer API 무한스크롤

by 은지:) 2023. 1. 8.
728x90
반응형
 

Intersection Observer API - Web API | MDN

Intersection Observer API는 타겟 요소와 상위 요소 또는 최상위 document 의 viewport 사이의 intersection 내의 변화를 비동기적으로 관찰하는 방법입니다.

developer.mozilla.org

 

한번에 이해하면 천재! 옵저버 공식문서 👆

 

 

 

let options = {
  root: document.querySelector('#scrollArea'),
  rootMargin: '0px',
  threshold: 1.0
}

let observer = new IntersectionObserver(callback, options);

 

 

observer API 는 매우 유용한 API 이다.

뷰포트와 관찰자를 설정해 그 둘이 만나는 지점의 true/false 값을 이용해 인터렉티브한 요소들을 마구 그려낼 수 있다.

 

공식 문서에서는 option의 threshold 에 퍼센트 배열을 넣고 가시성 비율

entry.intersectionRatio

을 사용해 박스가 보여지는 범위마다 색깔을 다르게 나타내는 UI 를 예시로 보여주었는데

catbow 라이브러리도 요런 원리로 돌아간다

 

 

 

이렇게보니 그 동안 무한 스크롤을 구현한 건 정말 observer API 의 10%만 써왔던 게 아닐까 싶다

다시 옵저버를 공부하고자 무한스크롤을 간단하게 다시 구현해보았는데

 

 

 

무한 스크롤을 거꾸로 만들었다 ㅋㅋㅋㅋㅋㅋ

flex-direction : column-reverse 로 데이터가 반대로 쌓이게 만들었다

 

 

다만 클린업 부분들 좀 다르게 주었는데 (https://kimjunho97.tistory.com/3 참고)

 

 

useEffect 인자성 빈 배열 안에 observer를 넣어 한번만 일어나게 했다.

observer 자체를 한번만 일으키니 굳이 클린업 작업이 필요 없다

 

 

그리고 만났던 다른 오류로는

 

전체 페이지를 viewPort로 잡지 않고 컴포넌트에 달기 위해 뷰포트를 따로 만들어주었는데

이 때문에 어디든지 관찰자가 잡혀 무한으로 스크롤되는... 오류를 만났다

 

이는 관찰자 크기를 10vw, rootMargin을 10px 늘려 오류를 고쳤다.

 

 

const Observer = () => {
  const [data, setData] = useState([]);
  const refObserver = useRef(null);
  const viewport = useRef(null);

  useEffect(() => {
    const options = {
      root: viewport.current,
      rootMargin: "0px 0px 10px 0px",
      threshold: 0.5,
    };

    const handleObserver = () => {
      axios.get("./data.json").then((res) => setData((pre) => [...pre, ...res.data]));
    };

    const observer = new IntersectionObserver((entries) => {
      entries.forEach((entry) => {
        if (entry.isIntersecting) {
          handleObserver();
        }
      });
    }, options);
    
  }, []);

  // eslint-disable-next-line react-hooks/exhaustive-deps
  console.log("data", data);
  return (
    <>
      <div
        ref={viewport}
        style={{
          padding: "5px",
          border: "1px solid black",
          overflow: "scroll",
          height: "100vh",
          display: "flex",
          flexDirection: "column-reverse",
        }}
      >
        <Layout>
          {data?.map((el, id) => {
            return <Container key={id} el={el} idx={id} />;
          })}
          <div style={{ background: "blue", width: "10vw", height: "1px" }} ref={refObserver} />
        </Layout>
      </div>
    </>
  );
};

export default Observer;

 

 

나중에 옵저버로 채팅창도 만들어 보고 싶다 ☺️

 

728x90
반응형

댓글