FrontEnd/기능구현

무한스크롤 (InfiniteScroll) - 리액트(React)

3일마다 작심3일 2021. 12. 21. 13:13

무한 스크롤이란?

무한 스크롤이란 뿌려주는 데이터가 너무 많아서 렌더링되는 양이 너무 많아 성능이 저하되는것을 막기위해

첫 스크롤 화면에는 적은양의 데이터만 렌더되게끔 만들어주고 사용자가 스크롤의 하단에 근접했을때 다음 데이터를 뿌려주는 기법(?) 중에 하나이다.

 

InfiniteScrollPage.js

import { useEffect, useState } from "react";
import "../App.css";
import InfiniteScroll from "../components/InfiniteScroll";

function PInfiniteScroll() {
  const [datas, setDatas] = useState([]);
  const [scrollOptions, setScrollOptions] = useState({
    childLength: 30, // 첫 렌더될 아이템의 개수
    fullHeight: 0, // 총 스크롤의 크기
  });
  const initialDatas = require("../json/infiniteScroll.json");

  useEffect(() => {
    setDatas(initialDatas.slice(0, scrollOptions.childLength));
  }, [initialDatas, scrollOptions.childLength]);

  return (
    <InfiniteScroll
      datas={datas}
      setDatas={setDatas}
      scrollOptions={scrollOptions}
      setScrollOptions={setScrollOptions}
    />
  );
}

export default PInfiniteScroll;

datas를 useState상태로 관리해주고 scrollOption에는 렌더될 아이템의 개수와 총 스크롤의 크기를 넣어준다.

useEffect로 화면이 초기에 렌더될때, childLength가 추가 되었을때를 생각해서 디펜던시에 추가해주도록한다.

 

 

InfiniteScroll.js

import { useCallback, useRef } from "react";
import "../App.css";

function InfiniteScroll({ datas, scrollOptions, setScrollOptions }) {
  const fullContent = useRef();
  const childContent = useRef();
  
  const onScroll = useCallback(
    (e) => {
      const scrollAreaHeight = fullContent.current.clientHeight; // 한 눈에 보이는 스크롤 영역
      const myScroll = e.target.scrollTop + scrollAreaHeight; // 사용자의 스크롤 위치
      const childHeight = childContent.current.clientHeight; // 스크롤안의 아이템의 높이
      scrollOptions.fullHeight = e.nativeEvent.target.scrollHeight;

      const showMoreData = () => {
          setScrollOptions({ ...scrollOptions,
          childLength : scrollOptions.childLength + 30,
          fullHeight : childHeight * scrollOptions.childLength
        })
      }

      myScroll === scrollOptions.fullHeight && showMoreData(); // 사용자의 스크롤 영역이 하단에 도달했을때 shoowMoreData함수를 실행시킨다.
    }, [scrollOptions, setScrollOptions]
  )

  return (
    <div className="scroll-container" onScroll={onScroll} ref={fullContent}>
      {datas?.map((data, index) => (
        <div key={index} className="content-contaienr" ref={childContent}>
          {data.title}
        </div>
      ))}
    </div>
  );
}

export default InfiniteScroll;

 

우선 Scroll영역의 height와, 실제 렌더될 아이템들의 height를 구해야하기 때문에 ref를 이용해서 높이를 가져와준다.

다행이 div의 속성중에 onScroll안에 event객체를 담고있어 scroll에 대한 정보로 쉽게 기능을 구현할 수 있다.

 

scrollAreaHeight 는 한 눈에 보이는 스크롤 영역 즉 그냥 height값을 구하는것이다.

 

myScroll이라는 변수에는 e.target.scrollBottom을 넣어서 사용자의 스크롤이 어디있는지 제어하고싶었으나 e.target.scrollTop밖에 없어 한눈에 보이는 스크롤 영역인 scrollAreaHeight의 값을 더 해 스크롤 하단의 위치 부분을 구했다.

 

childHeight는 스크롤에 렌더될 아이템들의 높이를 담고있는 변수다.

 

myScroll ==== scrollOptioons.fullHeight && showMoreData();

현재 사용자가 보고있는 스크롤 영역이 스크롤 옵션에 설정되어있는 하단 부분과 같다면 showMoreData함수를 실행시킨다.

 

showMoreData()

ScrollOption을 set해준다.

한 번에 렌더되는 아이템을 30개씩 늘려주고

하나의 아이템의 높이가 90px이라고 가정한다면

fullHeight 프로퍼티에는 각 렌더되는 아이템의 높이와 scrollOption에 설정되어있는 childLength만큼 곱해주면 

30 * 90 즉 2700만큼 fullHeight가 설정된다.