-
react-query의 useInfiniteQuery와 IntersectionObserver API를 이용하여 무한 스크롤 가져오기React.js 2023. 10. 15. 15:34728x90
{ "success": true, "data": { "list": [ { "feedId": "...", "contents": "...", "groupId": "...", "groupName": "...", "memberId": "...", "username": "...", "medias": [] }, { "feedId": "...", "contents": "...", "groupId": "...", "groupName": "...", "memberId": "...", "username": "..", "medias": [ { "id": "...", "url": "..." }, { "id": "...", "url": "..." } ] }, { "feedId": "...", "contents": "...", "groupId": "...", "groupName": "...", "memberId": "...", "username": "..", "medias": [ { "id": "...", "url": "..." }, { "id": "...", "url": "..." } ] }, ], "page": 1, "totalPage": 4 } }
먼저 backend에서 해당 feed를 줄때 page와 totalPage를 주어야한다. 다른방법이 있는지는 모르겠다.
const { data, fetchNextPage, isLoading, isError } = useInfiniteQuery( ['feeds'], async ({ pageParam = 1 }) => await FeedService.getFeeds(pageParam), { getNextPageParam: (lastPage, allPosts) => { return lastPage.page !== allPosts[0].totalPage ? lastPage.page + 1 : undefined; }, }, );
1. data : 성공 했을때 data에 결과값 받을 수 있다.
2. fetchNextPage : 마지막 페이지에 도달할때 호출하는 콜백 함수로써 getNextPageParam이랑 세트템이다.
3. isLoading: 로딩상태
4. isError: 에러상태
async ({ pageParam = 1 }) => await FeedService.getFeeds(pageParam),
pageParam 디폴트값 1 처음 페이지가 1이란뜻, getNextPageParam녀석으로 인해 계속 +1로 증가함.
await FeedService.getFeeds 백엔드에 api 호출하는 함수
getNextPageParam: (lastPage, allPosts)
lastPage: { list: FeedInfo[]; page: number; totalPage: number; }
allPosts: { list: FeedInfo[]; page: number; totalPage: number; }[]
typescript를 상요하면 리턴타입만 봐도 이생키가 머하는놈인지 쉽게 알 수 있다.
lastPage.page !== allPosts[0].totalPage ? lastPage.page + 1 : undefined;
결국 lastPage는 현재 불러온 feed의 page라는걸 알 수 있고 allPosts라는놈은 페이징 했던 놈들이 싹다 담기는 놈이라 생각하면 된다.
page1: allPosts[0],
page2: allPosts[1],
page3: allPosts[2],...
이런식
결국 useInfiniteQuery 이자식이 리턴 해주는 data라는 녀석이 무슨 녀석인지 알 수 있다. 내가 처음 대가리 아팟던 이유는
export interface FeedsResponse { list: FeedInfo[]; page: number; totalPage: number; } export interface FeedInfo { feedId: string; contents: string; groupId: string; groupName: string; memberId: string; username: string; }
await FeedService.getFeeds 함수의 리턴값이 이러한 형식인데
data를 찍어보면 pageParams와 pages 2개밖에 안나온다 ??? 그래서 뭐지 분명 data.list로 map 함수 돌리려고 했는데 띵했다.
InfiniteData<FeedsResponse>|undefined
이유는 useInfiniteQuery 이놈의 data의 InfiniteData 타입 때문이다. 이놈을 찾다보니 useInfiniteQuery 의 data라는놈은 결국
?page=1 일때 data.pages[0]
?page=2 일때 data.pages[1]
?page=3 일때 data.pages[2]
...
이런식으로 결과값이 담긴다.
그래서 react에서 map함수로 컴포넌트 돌릴때
{data?.pages.map((page, pageIndex) => ( <React.Fragment key={pageIndex}> {page.list.map(feed => ( <FeedItem key={feed.feedId} id={feed.feedId} /> ))} </React.Fragment> ))}
이런식으로 돌려줘야함!!
이제 핵심 기능인 무한 스크롤을 위해 useInfiniteQuery 의 fetchNextPage와 Intersection Observer API를 이용하면 된다.
isRefetching : 다음페이지 fetching 상태 이걸로 다음 페이지 가져올때 로딩 화면 보여줄 수 있음.
{isLoading && ( <div className="border border-customDark shadow rounded-md p-4 w-full mx-auto"> <div className="animate-pulse flex space-x-4"> <div className="rounded-full bg-slate-200 h-10 w-10"></div> <div className="flex-1 space-y-6 py-1"> <div className="h-2 bg-slate-200 rounded"></div> <div className="space-y-3"> <div className="grid grid-cols-3 gap-4"> <div className="h-2 bg-slate-200 rounded col-span-2"></div> <div className="h-2 bg-slate-200 rounded col-span-1"></div> </div> <div className="h-2 bg-slate-200 rounded"></div> </div> </div> </div> </div> )} {data?.pages.map((page, pageIndex) => ( <React.Fragment key={pageIndex}> {page.list.map(feed => ( <FeedItem key={feed.feedId} id={feed.feedId} /> ))} </React.Fragment> ))} {isRefetching && ( <React.Fragment> <div className="border border-customDark shadow rounded-md p-4 w-full mx-auto"> <div className="animate-pulse flex space-x-4"> <div className="rounded-full bg-slate-200 h-10 w-10"></div> <div className="flex-1 space-y-6 py-1"> <div className="h-2 bg-slate-200 rounded"></div> <div className="space-y-3"> <div className="grid grid-cols-3 gap-4"> <div className="h-2 bg-slate-200 rounded col-span-2"></div> <div className="h-2 bg-slate-200 rounded col-span-1"></div> </div> <div className="h-2 bg-slate-200 rounded"></div> </div> </div> </div> </div>
참고자료 - https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API
https://velog.io/@cnsrn1874/react-query-useInfiniteQuery
https://tanstack.com/query/v4/docs/react/reference/useInfiniteQuery
https://min-kyung.tistory.com/190
728x90'React.js' 카테고리의 다른 글
react에서 emoji 사용하기 위한 라이브러리 (0) 2023.10.28 lottie-react 라이브러리에서 typescript 사용시 lottieRef 프로퍼티 타입 (0) 2023.10.19 React + Typescript + Storybook (0) 2023.10.10 [라이브러리]React에서 드래그 이벤트 필요할 때 (0) 2023.07.25 읽기 전용 속성이므로 'current'에 할당할 수 없습니다.ts(2540) (0) 2023.02.03