-
React/InfiniteScroll - 외부 collectionLanguage/React 2022. 12. 2. 09:49
InfiniteScroll을 공통으로 사용하기 위해서 Component형식으로 만들고 나중을 위해서 정리해 둔다.
InfiniteScroll Component 외부에 collection을 두고 사용하는 방식
부모 컴퍼넌트
import React, { Component } from 'react'; ... class RequestViews extends Component { state = { infiniteResetUseEffect: false, // Infinite 외부에서 리스트를 초기화 해줘야 할 경우 변경 requests: [], ... }; fetchMoreList = (offset) => { return new Promise((resolve, reject) => { const pagination = { ...this.pagination, offset }; const params = { pagination: pagination, filters: this.filters }; Request.get('/requests?' + JSON.stringify(params)).then((response) => { this.setState({ requests: this.state.requests.concat(response.DATA) }); resolve(response.DATA); }); }); }; resetList = () => { this.setState({ resetInfiniteUseEffectFlag: !this.state.resetInfiniteUseEffectFlag }); }; ... render() { const { requests } = this.state; if (!requests) { return false; } return ( <> <div className="inner-content box-list-wrap"> ... <> <InfiniteScroll fetchMoreList={this.fetchMoreList} resetMoreList={() => { return new Promise((resolve, reject) => { this.setState({ requests: [] }, () => { resolve(); }); }); }} infiniteResetUseEffect={this.state.infiniteResetUseEffect} > {requests.length === 0 ? ( <div className="no-data"> <img src="/images/image_empty_box.svg" alt="" /> <div className="title"> {(() => { for (let answerYn of this.filters.IS_ANSWERED) { if (answerYn === 'N') { return <>접수된 </>; } } return <>처리된 </>; })()} 요청이 없습니다. </div> </div> ) : ( <div className="items-wrap"> <ul> {requests.map((request, idx) => ( <li className="box" key={request.G_REQUEST_NO}> <RequestView request={request} onRequestViewPop={(request) => { this.setState({ visibleRequestViewPop: true, request: request }); }} handleChangeStatus={(request) => { this.handleChangeStatus(request); }} /> </li> ))} </ul> </div> )} </InfiniteScroll> </> </div> <RequestViewPop open={this.state.visibleRequestViewPop} gRequestNo={this.state.request?.G_REQUEST_NO} onClose={() => this.setState({ visibleRequestViewPop: false, request: null })} onUpdate={() => { this.props.onUpdate(); this.resetList(); }} /> ... </> ); } } export default wrapWithBase(RequestViews);
자식 컴포넌트
import React, { useEffect, useState } from 'react'; const InfiniteScroll = (props) => { const [first, setFirst] = useState(true); // 최초 로드인지 const [loading, setLoading] = useState(false); // 리스트 가져오고 있는지 const [offset, setOffset] = useState(0); // back에 요청할 페이지 데이터 순서 정보 const [target, setTarget] = useState(null); // 관찰대상 target // const [isLoaded, setIsLoaded] = useState(false); // Load 중인가를 판별 (요청이 여러번 가는 것을 방지) const [isLoaded, setIsLoaded] = useState(true); // Load 중인가를 판별 (요청이 여러번 가는 것을 방지) const [stop, setStop] = useState(false); // 마지막 데이터까지 다 불러온 경우 더이상 요청을 보내지 않기 위해서 useEffect(() => { // console.log('마운트 될 때만 실행'); setFirst(false); }, []); // React Hooks: componentWillReceiveProps (props.infiniteResetUseEffect 변할 때 실행) useEffect(() => { if (first === false) { // 초기화 setIsLoaded(false); setStop(false); setOffset(0); } }, [props.infiniteResetUseEffect]); useEffect(() => { if (first === false && offset === 0) { (async () => { await props.resetMoreList(); fetchMorderList(); })(); } }, [offset]); useEffect(() => { let observer; if (target && !stop === true) { // callback 함수로 onIntersect를 지정 observer = new IntersectionObserver(onIntersect, { threshold: 1, }); observer.observe(target); } return () => observer && observer.disconnect(); }, [target, isLoaded]); // isLoaded가 변할 때 실행 useEffect(() => { // isLoaded가 true일 때 + 마지막 페이지가 아닌 경우 = 요청보내기 if (isLoaded === true && !stop === true) { fetchMorderList(); } }, [isLoaded]); const fetchMorderList = async () => { if (loading === true) { return false; } setLoading(true); // 데이터를 받아오도록 true 로 변경 const list = await props.fetchMoreList(offset); // 다음 요청할 데이터 offset 정보 setOffset((offset) => offset + list.length); // 다음 요청 전까지 요청 그만 보내도록 false로 변경 setIsLoaded(false); if (list.length < 3) { // 전체 데이터를 다 불러온 경우(불러온 값이 12개 보다 적다면 -> 매번 12개씩 불러오기로 했으므로 해당 값보다 작으면 마지막 페이지) 아예 로드를 중지 setStop(true); } setLoading(false); }; // callback const onIntersect = async ([entry], observer) => { // entry 요소가 교차되거나 Load중이 아니면 if (entry.isIntersecting && !isLoaded === true) { // 관찰은 일단 멈추고 observer.unobserve(entry.target); // 데이터를 받아오도록 true 로 변경 setIsLoaded(true); // 불러온 후 다시 관찰 실행 observer.observe(entry.target); } }; return ( <> {props.children} <div ref={setTarget}></div> </> ); }; export default InfiniteScroll;
참조)
https://velog.io/@ohsg97/React-infinite-scroll
'Language > React' 카테고리의 다른 글
NextJS에서 queryString 변경 감지 hook 만들기 (0) 2023.10.20 React Loop 모음 (0) 2023.05.11 React/InfiniteScroll - 내부 collection (0) 2022.12.02 React/RadioButton Component 만들기 (0) 2022.12.01 React/X-Bar chart 만들기 (0) 2022.12.01