[TIL] Day-27

yoon Y·2021년 9월 13일
0

데브코스 - TIL

목록 보기
14/19

무한 스크롤 UI구현하기

1.scrollEvent

window의 scroll이벤트를 통해 스크롤링이 일어날 때마다 화면 전체의 높이와 스크롤 위치를 통해 스크롤이 컨텐츠 끝 즘에 다다랐는지 체크해서 처리하는 방식

App컴포넌트

  this.state = {
    limit: 5,
    nextStart: 0, 
    photos: [],
    totalCount: 0,
    isLoading: false,
  };

fetchPhotos()의 내부 과정과 발생 시점

  • 전체 데이터 갯수를 저장하고 첫 데이터들을 불러온다
  • 데이터를 불러올 때
    • fetch함수 실행 전 isLoading을 true로 setState해준다.
    • 현재 범위의 데이터를 불러온 후, setState를 해준다.
 this.setState({
      ...this.state,
      nextStart: nextStart + limit, // nextStart가 0으로 시작해서 limit만큼 계속 더해짐 시작점이 0,5,10,15,20...로 증가
      photos: [...this.state.photos, ...photos], // 이미 불러와진, 전 상태에 새로 받은 데이터를 누적해서 상태를 변경해줌
      // photos: this.state.photos.concat(photos), // 두 배열 요소들을 합친 새로운 배열을 반환함
      isLoading: false, // 로딩 중을 해제해 줌
    });
  • 콜백함수를 이용해 자식 컴포넌트에서 스크롤 이벤트가 발생했을 경우 fetchPhotos()를 실행한다.
onScrollEnded: () => {
      fetchPhotos(); // 이벤트가 실행될 때 불러짐
    },

PhotoList컴포넌트

새로운 요소만 렌더링 하기

누적된 데이터로 화면을 렌더링 할 경우 데이터가 바뀔때마다 전부 새롭게 렌더링 되어 화면 위로 올라가게 된다.

  • Ul을 만들고 가져온다.
  • 데이터를 순회하며 li를 만드는데, 현 데이터에 포함된 id를 data-id속성으로 가지고 있지 않을 때에만 새롭게 생성한다.
  • ul에 appendChild해준다.
photos.forEach((photo) => {
      //있는 거는 거르고 새로운 것만 렌더링
      // 데이터를 돌면서 photo의 id 기준으로 렌더링이 되어있는지 체크 (data-id사용)
      // 현재 ul의 li중 데이터에 포함된 id가 데이터 속성이 아니라면 새로 생성한다
      // 이 id로 만들어진 li가 있냐? 없으면 만들어
      if ($photos.querySelector(`li[data-id="${photo.id}"]`) === null) {
        // 없으면 li생성하고 $photos에 appendChild
        const $li = document.createElement('li');
        $li.setAttribute('data-id', photo.id);
        $li.style = 'list-style:none;';
        $li.innerHTML = `<img width="100%" src="${photo.imagePath}"/>`;

        $photos.appendChild($li);
      }
    });

새로운 데이터를 불러오는 조건

1.스크롤이 끝났고
2.데이터를 불러오는 중이 아니고
3.전테 데이터가 다 불러지기 전이라면

const isScrollEnded =
      window.innerHeight + window.scrollY + 100 >= document.body.offsetHeight; 
    // 1.브라우저의 내부 높이 + 문서가 수직으로 얼마나 스크롤됐는 지 픽셀 단위로 반환된 값 - 스크롤된 전체 브라우저 높이
    // 2.body컨텐츠 높이
    // body보다(자식 요소들을 모두 합친 높이) 브라우저 상의 높이가 더 커질 떄 데이터를 더 불러옴

if (isScrollEnded && !isLoading && photos.length < totalCount) {
      onScrollEnded();
    }

2.intersection observer

뷰포트에서 내가 감시하고 있는 애(observer)가 노출이 됐냐 안됐냐, 얼마나 됐냐를 가지고 판단하여 새로운 데이터를 불러오는 방법

observer객체 생성

  • 인자로 콜백 함수와 객체 2개를 받는다.
  • 콜백함수 entries.forEach문 안에 데이터가 불러지는 조건을 작성해준다.
  • 2번째 인자로 threshold를 설정해 옵저버의 노출 범위를 설정해줄 수 있다.
const oberver = new IntersectionObserver(
    (entries) => {
      entries.forEach((entry) => {
        if (entry.isIntersecting && !this.state.isLoading) {
          // 감찰자 요소가 화면에 보인다면 && 로딩중이 아니라면 다음 데이터 요청
          console.log('화면 끝!!', entry);
          // 전체 데이터가 다 불러와지지 않았을 때만 데이터 요청
          if (this.state.totalCount > this.state.photos.length) {
            onScrollEnded();
          }
        }
      });
    },
    {
      threshold: 1,
      // 0은 옵저버가 시작될 때,
      // 1은 옵저버가 화면에 전부 들어왔을 떄
    }
  );

observer 설정하기

  • 리스트 렌더링을 다 한 후 마지막 요소를 nextLi에 저장한다.
let $lastLi = null;
const $nextLi = $photos.querySelector('li:last-child');
  • 기존에 들어있던 observer를 삭제하고 새로운 요소(위에 저장한 nextLi)를 등록한다.
// 감찰자를 계속 바꿔줌
    if ($nextLi !== null) {
      if ($lastLi !== null) {
        oberver.unobserve($lastLi); // 옵저버에 마지막 요소들이 계속 쌓인다 - 없애줘야 함, 안그러면 기존 옵저버들이 보여질 때마다 데이터 요청 된다
      }
      $lastLi = $nextLi;
      oberver.observe($nextLi);
    }

무한 스크롤 구현 방법을 알고 싶었는데 이번에 배우게 되어서 좋았다.
이번 강의에서 특히 좋았던 점은 이미 렌더링된 요소들을 제외하고 렌더링 시키는 방법이었다. 노션 클론 프로젝트에서 페이지 목록 리스트들이 데이터가 변화할 때마다 모두 렌더링되어서 골치가 아팠는데 이번에 배운 걸 적용해봐야겠다.

profile
#프론트엔드

0개의 댓글