일단 컴포넌트는 어떤 식의 구조로 되어있냐면
최상위 부모 : Cert.js
부모 : CompleteCert.js
자식 : CompleteItem.js
react 17, 바닐라js는 정보가 많이 나와있는데 react15는 정보가 많이 없어서 적는다.
CompleteCert.js
에서 CompleteItem
컴포넌트를 map 돌릴 때에
이미 페이지 1개당 50개의 데이터를 받는다.
그리고 #observer
에 닿으면 Intersection Observer API
에 의해 감시당하면서
scroll 위치를 저장하고 추가적인 데이터를 불러오기위한 API가 호출된다.
이때 기존 값과 새로 받은 값을 더해서 completeCerts
에 저장한다.
해당 API가 호출되어 redux
에서 관리하고있던 completeCerts
가 이전 값과 달라지면 바뀐 스크롤 값을 저장하여 스크롤을 다시 이전 위치(마지막으로 보고있었던 사용자의 스크롤 위치)로 돌려준다.
getCompleteList = (page_no, scrolled) => {
let oldCompleteCerts = [];
if(isScrolled){ //스크롤을 맨 밑에 닿은 경우
oldCompleteCerts = this.props.completeCerts;
}
this.handleCheckedCancelSuccessCompletedList(certListByComplete, oldCompleteCerts);
}
handleCheckedCancelSuccessCompletedList = (list, oldList) => {
const { setCerts, completeCount } = this.props;
let oldCerts = _.defaultTo(oldList,[]);
let newList = [];
if(oldCerts.length > 0){
newList = [...oldCerts , ...newList];
}
setCerts({
completeCerts: newList,
completeCount : completeCount,
});
}
render() {
return (<div className='mmt_main_wrap'>
<CompleteCert
getCompleteList={this.getCompleteList}
/>
</div>)
}
constructor(props) {
super(props);
this.state = {
pageNo: 1,
scrollPosition: 0
};
}
componentDidMount() {
this.observer = new IntersectionObserver(this.handleObserver, {
threshold: 0,
});
const observerTarget = document.getElementById("observer");
if (observerTarget) {
this.observer.observe(observerTarget);
}
}
componentDidUpdate(prevProps, prevState, snapshot) {
const {scrollPosition} = this.state;
if (prevProps.completeCerts !== this.props.completeCerts && scrollPosition !== 0) {
const scrollable = document.querySelector('.mmt_main_wrap');
scrollable.scrollTop = scrollPosition;
}
}
componentWillUnmount() {
const observerTarget = document.getElementById("observer");
if (observerTarget && this.observer) {
this.observer.unobserve(observerTarget);
}
}
// Intersection Observer 설정
/**
handleObserver: 교차점이 발생했을 때 실행되는 콜백 함수.
entries: 교차점 정보를 담는 배열
isIntersecting: 교차점(intersection)이 발생한 요소의 상태
교차점이 발생하면 page 1 증가
*/
handleObserver = (entries) => {
const target = entries[0];
const observer = document.querySelector("#observer");
if (target.isIntersecting && this.props.progressCount === 0) {
let totalPages = this.props.completeCount / 50 + 1;
let plusedPageNo = this.state.pageNo + 1;
if (plusedPageNo <= totalPages) {
this.setState({pageNo: plusedPageNo, scrollPosition: observer.offsetTop}, () => {
this.props.getCompleteList(plusedPageNo, true);
});
}
}
};
render(){
return (
<div className="list_body">{completeCerts ? completeCerts.map((it, index) =>
<CompleteItem {...it}
history={history}
nahagoHRTno={nahagoHRTno}
key={index}/>) : null}
</div>
<p id="observer"/>
)
}