스로틀을 사용하였다. useCallback 을이용해서 스로틀함수를 생성하였으며, dependecy를 추가해서,dependency에따라 스로틀함수를 재생성했고,
클린업 함수를 통해 과거시점의 스크롤 이벤트는 remove해주고 , 현재시점의 스크롤이벤트를 등록해주었다. 그렇게해야 스로틀 함수에서 현재 렌더링시점의 state값을 참조 할 수 있다.
또한 메모리 최적화를 위해 throttle.cancel()을 사용해서, 이벤트 큐에 등록되어있는 스로틀함수중에서 과거시점에 등록된 스로틀함수들을 모두 지워주었다.
import React,{Fragment,useEffect,useCallback,useRef} from 'react'
import _ from 'lodash';
import InfiniteSpinner from './InfiniteSpinner';
function InfinityScroll(props) {
const dependency = [props.children,props.callNext,props.isNext,props.isLoading];
const _throttle = _.throttle(()=>{
if(props.isLoading){
return
}
let scrollHeight= document.documentElement.scrollHeight;
let scrollTop = document.documentElement.scrollTop
let clientHeight = document.documentElement.clientHeight;
if(scrollHeight-scrollTop-clientHeight < 300){
props.callNext(); // 데이터 fetch로직
}
},1000);
const throttle = useCallback(_throttle,dependency);
useEffect(()=>{
if(props.isLoading){
return;
}
window.addEventListener('scroll', throttle);
return ()=> {
window.removeEventListener('scroll',throttle);
throttle.cancel();
}
},dependency);
return (
<Fragment>
{props.children}
{props.isNext ? <InfiniteSpinner/>:null }
</Fragment>
)
}
InfinityScroll.defaultProps = {
children:null,
callNext:()=>{},
isNext:null,
isLoading:false
}
export default InfinityScroll
위에 제가 사용한 의존성은, 리덕스 스토어의 데이터들 입니다.
props.isNext: 리덕스와 통신하여 다음 fetch할 데이터가 있는지 저장해놓은 리덕스 스토어 데이터
props.isLoading: 현재 데이터 fetch 중인지를 의미
props.callNext: 파이어스토어에 접근하는 함수를 호출하는 함수
if(scrollHeight-scrollTop-clientHeight < 300){
props.callNext();
}
scrollHeight-scrollTop-clientHeight 을 통해 현재 스크롤화면이 브라우저의 맨하단에서 얼만큼 떨어져 있는지 계산해준다. 맨 아래 남은 px이 300px 이하가되면 바닥에 닿았다고 판단하고 다음 데이터를 fetch 요청한다.