[React] Infinite Scroll

dosilv·2021년 5월 5일
2
post-thumbnail


인스타그램 클론에 리액트를 씌우면서 infinite scroll(스크롤이 내려감에 따라 요소가 계속 추가되는 것)을 구현하고 싶었다. 사실 그동안 click, input, change 같은 이벤트만 주구장창 쓰고 scroll은 한 번도 사용한 적 없기 때문에... 구글링으로 어찌어찌 하긴 했는데😏 내 걸로 만들기 위해서 제대로 해 보는 정리!


🍇 scroll과 관련된 property

  • scrollHeight: 스크롤바에 의해 감춰진 영역을 포함한, 전체 컨텐츠의 높이
  • clientHeight: 테두리(스크롤 화면) 영역의 높이
  • scrollTop: 스크롤이 아래로 움직임에 따라 가려진 컨텐츠의 높이

페이지 전체를 요소로 잡으면 세 property 모두 document.documentElement.___로 접근할 수 있음!

만약 브라우저 창의 크기를 조정하면 clientHeight는 변화하지만, scrollHeight은 컨텐츠의 높이이기 때문에 변하지 않는다.

그리고 스크롤 이벤트가 발생해도 scrollHeightclientHeight불변인 반면, scrollTop은 스크롤바가 맨 위에 있다면 0, 맨 아래에 도달하면 (scrollHeight-clientHeight)가 된다. (clientTop과 같은 테두리가 없다고 가정할 때!)

그래서 윈도우에 addEventListener로 스크롤 이벤트를 감지하고, 스크롤이 맨 밑에 도달했을 때, 즉 scrollTop >= scrollHeight-clientHeight이 되면 다시 fetch함수로 추가 데이터를 불러오도록 하면 됨!

처음부터 차근차근 코드로 풀어보자.


🍇 Infinite Scroll 구현

  1. 우선 state에fetch된 총 feed data를 담을 빈 배열(='feedData')초기에(컴포넌트가 마운트될 때) fetch될 feed의 수(='items'), 그리고 fetch가 일어날 때마다 누적될 feed의 수(='preItems')를 정의한다.
this.state = {
  feedData: [],
  items: 2,
  preItems: 0,
};
  1. componentDidMount에서는 slice를 이용해 컴포넌트가 마운트될 때 설정한 수만큼 데이터를 fetch해오도록 하고, setState로 feedData에 담아 준다.
    그리고 스크롤 이벤트를 감지할 수 있도록 window에 eventListner를 걸어 주고, 이벤트가 발생하면 infiniteScroll이라는 함수를 호출하도록 한다.
componentDidMount() {
    fetch('http://localhost:3000/data/Doeun/articleData.json', {
      method: 'GET',
    })
      .then(res => res.json())
      .then(res => {
        let dataToAdd = res.slice(this.state.preItems, this.state.items);
        this.setState({
          feedData: [...this.state.feedData, ...dataToAdd],
        });
      });
    window.addEventListener('scroll', this.infiniteScroll);
  }
  1. 그리고 위에서 설명했던 원리에 따라 infiniteScroll을 정의해 준다. 우선 documentElement의 scrollHeight, scrollTop, clientHeight를 구조분해할당해 주고, scrollTop이 scrollHeight-clientHeight보다 같거나 커질 때(=스크롤이 맨 아래에 도달할 때) preItems를 items로 갱신하고, items에는 새로 추가할 데이터의 개수를 더한 뒤 다시 데이터를 fetch하도록 한다.
infiniteScroll = () => {
  const { scrollHeight } = document.documentElement;
  const { scrollTop } = document.documentElement;
  const { clientHeight } = document.documentElement;

  if (scrollTop >= scrollHeight - clientHeight) {
    this.setState({
      preItems: this.state.items,
      items: this.state.items + 1,
    });
    fetch('http://localhost:3000/data/Doeun/articleData.json', {
      method: 'GET',
    })
      .then(res => res.json())
      .then(res => {
        let dataToAdd = res.slice(this.state.preItems, this.state.items);
        this.setState({
          feedData: [...this.state.feedData, ...dataToAdd],
        });
      });
  }
};
  1. 마지막으로 컴포넌트가 언마운트될 때 eventListner를 지워 준다.
componentWillUnmount() {
  window.removeEventListener('scroll', this.infiniteScroll);
}

그런데 위에서 componentDidMount와 infiniteScroll 내부에 fetch부분이 중복되기 때문에! getData라는 함수로 대체해 주면 최종적으로 아래처럼 정리할 수 있다.

componentDidMount() {
  this.getData();
  window.addEventListener('scroll', this.infiniteScroll);
}
  

infiniteScroll = () => {
  const { scrollHeight } = document.documentElement;
  const { scrollTop } = document.documentElement;
  const { clientHeight } = document.documentElement;
  if (scrollTop >= scrollHeight - clientHeight) {
    this.setState({
      preItems: this.state.items,
      items: this.state.items + 1,
    });
    this.getData();
  };
  
  
getData = () => {
  fetch('http://localhost:3000/data/Doeun/articleData.json', {
    method: 'GET',
  })
    .then(res => res.json())
    .then(res => {
      let dataToAdd = res.slice(this.state.preItems, this.state.items);
      this.setState({
        feedData: [...this.state.feedData, ...dataToAdd],
      });
    });
}
  

componentWillUnmount() {
  window.removeEventListener('scroll', this.infiniteScroll);
}

사실 페이지 전체를 다루기 위해서는 document.documenElement의 scrollHeigt/scrollTop/clientHeigth만 고려하는 것이 위험하다고 해서 document.body의 값과 비교해 더 큰 값을 사용하도록 하긴 했는데... 내 쪼그만 클론 페이지에서는 이 부분이 딱히 문제가 되지 않아서 뭐가 위험한지 아직 정확히 체감하지 못했다....😶 더 공부가 필요할 것 같다~~~!



🙇‍♀️ 참고한 자료

요소 사이즈와 스크롤
TIL 56 | React로 무한스크롤 구현

profile
DevelOpErUN 성장일기🌈

5개의 댓글

comment-user-thumbnail
2021년 5월 16일

덕분에 스크롤 이벤트 구현하는데 많은 도움 되었습니다 도은님~! 👍👍

1개의 답글
comment-user-thumbnail
2021년 5월 16일

도은님 저도 프로젝트때문에 구글링하고 있었는데 잘보고 갑니다,, 🥺 다른 블로그보다 더욱 이해 쏙쏙,, 👍🏻

1개의 답글
comment-user-thumbnail
2022년 10월 23일

우와 이걸 Infinite Scroll이라고 부르는군요! 용어 잘 알아갑니다

답글 달기