Wanted OnBoarding #1 Infinite Scrolling

JohnKim·2021년 7월 27일
0

React

목록 보기
10/11
post-thumbnail

어떤 기능인가?📌

스크롤을 일정부분 내리게 되면 더많은 데이터 및 정보가 밑으로 쌓이게 된다.

Infinite Scrolling 즉, 무한스크롤이란 계속 스크롤을 내리면서 정보를 탐색할 수 있는 기능을 말한다.

어떻게 구현 했을까? 🔑

무한스크롤을 구현하기위해 구글에 검색한 결과 크게 두가지 방법을 사용하였다.

scoll eventIntersectionObserver API를 사용하는것! 📝

scoll event?

document.documentElement에 접근하면 html geometry값에 접근이 가능하다.

geometry값을 알기쉽게 정리해놓은 이미지이다. ( 출처:javascript.info)

우선 내가 사용할 스크롤 이벤트는 가로축이 필요없다.

그러므로 세로축과 관련된
clientHeight, offsetHeight, scrollHeight, scrollTop, offsetTo을 모두 console.log()로 찍어 보았다.

offsetHeigth , offset 은 고정값이므로 제외, 나머지들을 살펴보면

clientHeight는 컨텐츠의 높이이고 scrollTop은 최상단과 컨텐츠의 거리이고

scrollHeight은 전체 높이이다.

clientHeight + scrollTop === scrollHeight이라면 스크롤이 최하단에 위치한다는 것을 의미한다.🛎

그렇다면 위와같은 조건이 충족 만족되었을 때 state를 변경하여 쿼리 파라미터에 넣어주면 끝

const [page, setPage] = useState(1);
const [items, setItems] = useState([]);

const FetchData = () => {
  // 쿼리 파라미터 사용하고 적절한 곳에 state를 넣어준다.
    const url = `https://jsonplaceholder.typicode.com/comments?_page=${page}&_limit=10`;
    fetch(url)
      .then((res) => res.json())
  // 기존 배열에 새로 패치한 데이터를 추가한다.
      .then((item) => setItems((prev) => [...prev, ...item]));
  };

useEffect(() => {
    window.addEventListener("scroll", onScroll);
    return () => {
      window.removeEventListener("scroll", onScroll);
    };
  }, []);

  const onScroll = () => {
    const scrollTop = document.documentElement.scrollTop;
    const clientHeight = document.documentElement.clientHeight;
    const scrollHeight = document.documentElement.scrollHeight;


    if (scrollTop + clientHeight === scrollHeight) {
      // 조건 충족시 page 추가
      setPage((prev) => prev + 1);
    }

단점

scroll 이벤트로 무한스크롤을 구현하게 될 때 단점은 한번 스크롤을 할 때마다 수많은
console.log()가 찍히는걸 볼 수 있다.

IntersectionObserver API👍

Intersection observer을 통해 설정한 요소의 교차점을 관찰 할 수 있고 요소가 뷰포트에 포함되는지 , 사용자 화면에 지금 보이는 요소인지 구별하는 기능을 제공한다.

이 기능은 비동기적으로 실행되기 때문에, scroll 같은 이벤트 기반의 요소 관찰에서 발생하는 렌더링 성능이나 이벤트 연속 호출 같은 문제 없이 사용할 수 있다.

참고자료 https://heropy.blog/2019/10/27/intersection-observer/

--------------------- 사용후기--------------------- 📦

우선 scroll Event 보다는 좀더 난이도가 있었다.

요소의 가시성을 고려하고, 교차점을 관찰 해야 했으며 Intersection observer API의 콜백함수 의 인자인 entries안에 다양한 방법이 숨어있다. 그 방법들은 위의 참고자료안에 있다.

나는 그중에서 isIntersecting: 관찰 대상의 교차 상태(Boolean)를 선택하였다.

관찰 대상이 루트 요소와 교차 상태로 들어가거나(true) 교차 상태에서 나가는지(false) 여부를 나타내는 값(Boolean)이다.

교차상태로 요소를 지목해야하기 때문에 useRef를 사용하였다.

이후 설명은 코드를 보면서 진행한다.

코드

const [feedData, setFeedData] = useState([]);
  const [pageNumber, setPageNumber] = useState(1);
  const [loading, setLoading] = useState(false);
  const pageEnd = useRef();

  const fetchFeeds = async (pageNumber) => {
    const res = await fetch(
      `https://jsonplaceholder.typicode.com/comments?_page=${pageNumber}&_limit=10`
    );
    const data = await res.json();
    // 새로운 데이터를 기존 배열에 넣기
    setFeedData((prev) => [...prev, ...data]);
    // loading true를 통해 옵져버를 컨트롤한다.
    setLoading(true);
  };

  useEffect(() => {
    fetchFeeds(pageNumber);
  }, [pageNumber]);

  const loadMore = () => {
    setPageNumber((prevPageNumber) => prevPageNumber + 1);
  };

  useEffect(() => {
    //useRef로 지정한 요소가 아니면 패스!
    if (!pageEnd.current) {
      return;
    }
    
    if (loading) {
      //옵저버를 쓰는 방법
      const observer = new IntersectionObserver(
        (entries) => {
          //entries의 첫번째 요소의 isIntersecting를 확인한다. 궁금하면 console.log(entries)!
          if (entries[0].isIntersecting) {
            // 기존 쿼리값에서 +1해주는 함수
            loadMore();
          }
        },
//옵저버가 실행되기 위해 타겟의 가시성이 얼마나 필요한지 백분율로 표시한다. 1은 100%이다.
        { threshold: 1 }
      );
		//관찰대상 정하기
      observer.observe(pageEnd.current);

      // 옵저버 연결해제
      return () => observer && observer.disconnect();
    }
  }, [loading]);

내가 원하는 순간에 효율적으로 작동하는 무한스크롤을 사용하기위해서는 IntersectionObserver API를 사용하면 된다!😎

참고 https://www.youtube.com/watch?v=nE4zY6PY748

0개의 댓글