이번 프로젝트에서 인스타그램과 비슷한 UI를 기획하였기 때문에 피드에 무한 스크롤 기능을 추가해야 했다.
프로젝트에 이미 리액트 쿼리를 도입한 상태기 때문에 리액트 쿼리에서 제공하는 useInfiniteQuery를 사용하여 구현해 보고자 했다.
npm install @tanstack/react-query @tanstack/react-query-devtools
위의 명령어를 입력하면 필요한 라이브러리를 설치할 수 있다.
리액트 쿼리는 버전이 높아지며 Vue.js나 앵귤러 등도 지원하게 되어 이름이 tanstack으로 바뀌었다고 한다.
처음에 서로 다른건줄 알고 리액트 쿼리를 3버전으로 설치했었다..
현재는 v5 를 이용할 수 있다.
1. useInfiniteQuery 란?
무한 스크롤을 구현하려면 useInfiniteQuery를 이용해야한다.
useQuery 와 비슷하지만 반환하는 데이터 모양이 다르다. infinite는 data와 page를 제공한다.
pages는 각 데이터 페이지를 나타내는 객체의 배열이다.
pageParams 는 page에서 사용하는 데이터를 나타내기 위한 변수이다.
검색된 쿼리의 키를 추적할 수 있다. 잘 사용하지는 않는다
아래와 같은 문법으로 사용 가능하다. 주석으로 설명을 달아놓은 것들 위주로 확인하면 된다.
const {
fetchNextPage, // 다음페이지를 가져옴(마지막까지 스크롤 한 경우, 추가버튼을 클릭한 경우)
fetchPreviousPage,
hasNextPage, // 다음 페이지가 있는지, undefined면 false를 반환
hasPreviousPage,
isFetchingNextPage, // 데이터를 가져오는 중인지 구별
isFetchingPreviousPage,
...result
} = useInfiniteQuery({
queryKey, // 쿼리 키
queryFn: ({ pageParam }) => fetchPage(pageParam), // 반환할 함수
initialPageParam: 1, // 디폴트 파라메터
...options,
getNextPageParam: (lastPage, allPages, lastPageParam, allPageParams) =>
lastPage.nextCursor, // 마지막 페이지 데이터나, 모든 페이지의 데이터에서 페이지를 가져옴
getPreviousPageParam: (firstPage, allPages, firstPageParam, allPageParams) =>
firstPage.prevCursor,
})
2. useInfiniteQuery 호출 작성하기
예를들어 스타워즈 api (swapi)를 호출하여 본다고 하면 아래와 같이 작성할 수 있다.
실제 첫번째 데이터가 있는 부분은 pages의 0번째 결과를 불러오면 된다.
import InfiniteScroll from "react-infinite-scroller";
import { Person } from "./Person";
import { useInfiniteQuery } from "@tanstack/react-query";
const initialUrl = "https://swapi.dev/api/people/";
const fetchUrl = async (url) => {
const response = await fetch(url);
return response.json();
};
export function InfinitePeople() {
const { data, fetchNextPage, hasNextPage } = useInfiniteQuery({
queryKey: ["sw-people"],
queryFn: ({ queryParam = initialUrl }) => fetchUrl(queryParam),
getNextPageParam: (lastpage) => {
return lastpage.next || undefined;
},
});
console.log(data.pages[0].results[0]);
return <InfiniteScroll />;
}
4. InfiniteScroll 컴포넌트
받아온 데이터를 InfiniteScroll 컴포넌트와 결합하여 보여줄 수 있다.
https://www.npmjs.com/package/react-infinite-scroller
npm install react-infinite-scroller --save
5. useInfiniteQuery 페칭과 에러 상태 처리
import InfiniteScroll from "react-infinite-scroller";
import { Person } from "./Person";
import { useInfiniteQuery } from "@tanstack/react-query";
const initialUrl = "https://swapi.dev/api/people/";
const fetchUrl = async (url) => {
const response = await fetch(url);
return response.json();
};
export function InfinitePeople() {
const {
data,
fetchNextPage,
hasNextPage,
isFetching,
isLoading,
isError,
error,
} = useInfiniteQuery({
queryKey: ["sw-people"],
queryFn: ({ queryParam = initialUrl }) => fetchUrl(queryParam),
getNextPageParam: (lastpage) => {
return lastpage.next || undefined;
},
});
if (isLoading) {
return <div className="loading">🔥 로딩중....</div>;
}
if (isError) {
return <div className="error">에러 발생 {error.toString()}</div>;
}
return (
<>
{isFetching && <div>💥 로딩중 ... </div>}
<InfiniteScroll
loadMore={() => {
if (!isFetching) {
fetchNextPage();
}
}}
hasMore={hasNextPage}
loader={
<div className="loader" key={0}>
Loading ...
</div>
}
>
{data.pages.map((pageData) => {
return pageData.results.map((person) => {
return (
<Person
key={person.name}
name={person.name}
hairColor={person.hair_color}
eyeColor={person.eye_color}
/>
);
});
})}
</InfiniteScroll>
</>
);
}
페이지 끝에서 다음 페이지가 있을 경우 lastpage의 next 값을 가져와 다음 param으로 적용한다. undefined인 경우 hasNextPage 값이 false로 반환 된다.
6. 요약
+ 양방향 스크롤링도 가능하다
중간부터 시작할때, 이때는 previous 이용하면 된다.
리액트 쿼리는 무한스크롤 시 많은 기능을 제공한다.
페이지네이션을 하거나 컴포넌트에서 페이지를 처리할 수 있다.
pageParam: 가져와야 할 다음 페이지, getNextPageParam옵션을 통해 관리된다. lastPage/allPages 매개변수를 사용한다. 반환된 데이터 양식에 따라 선택하면 된다.
hasNextPage: boolean 값, pageParam이 정의되어 있으면 true, undefined 이면 false를 반환한다.
때문에 더 불러올 수 있는 데이터가 있는지 확인이 가능하다.
fetchNextPage: 컴포넌트가 데이터를 불러와야 할때를 결정하고 hasNextPage 값으로 언제 그만 가져올 지 지정해 줄 수 있다.
'React' 카테고리의 다른 글
[React] 오류 해결 : 오류 'JSX.IntrinsicElements' 형식에 'div' 속성이 없습니다.ts(2339) (0) | 2024.04.25 |
---|---|
[React] 2024 CSS 스타일의 패러다임 (0) | 2024.04.22 |
[React] React Query v5 - 페이지네이션과 프리페칭(pre-fetching) (0) | 2024.04.20 |
[React] React Query 란? (0) | 2024.04.20 |
[React] Typescript + React Redux 프로젝트에 적용하기 (0) | 2024.04.19 |