무한스크롤 (InfiniteScroll) - 리액트(React)
무한 스크롤이란?
무한 스크롤이란 뿌려주는 데이터가 너무 많아서 렌더링되는 양이 너무 많아 성능이 저하되는것을 막기위해
첫 스크롤 화면에는 적은양의 데이터만 렌더되게끔 만들어주고 사용자가 스크롤의 하단에 근접했을때 다음 데이터를 뿌려주는 기법(?) 중에 하나이다.
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가 설정된다.