이번엔 다음 및 이전 페이지로 이동을 위한 버튼을 구현해보자.
그러기 위해서는 React Query의 Pagination을 이용하면 된다.
Pagination은 currentPage 상태를 통해 현재 페이지를 파악하는 페이지 매김 스타일이다.
이 방법은 댓글을 작업했듯이 페이지마다 다른 쿼리키가 필요하다.
따라서 쿼리키를 배열로 업데이트해서 가져오는 페이지 번호를 포함하면 된다.
만약 사용자가 다음 혹은 이전 페이지로 가는 버튼 누르면 current Page 상태를 업데이트하면 되며, React Query가 바뀐 쿼리키를 감지하고 새로운 쿼리를 실행해서 새 페이지를 표시한다.
아래와 같이 currentPage를 쿼리키에 포함시키고, currentPage 상태가 변경되면 React Query가 바뀐 쿼리 키를 감지하여 새 쿼리키에 대한 데이터를 업데이트 할것이다. 당연히 이때는 쿼리함수도 업데이트를 해주어야할 것이다.
// Post.jsx
const [currentPage, setCurrentPage] = useState(1);
...
const {data, isError, isLoading, error} = useQuery(
["posts", currentPage],
() => fetchPosts(currentPage), {
staleTime : 2000
});
...
<button
disabled={currentPage <= 1}
onClick={() => {setCurrentPage(previousValue => previousValue -1);
}}>
이전 페이지
</button>
<button
disabled={ currentPage >= maxPostPage}
onClick={() => { setCurrentPage(previousValue => previousValue + 1);
}}>
다음페이지
</button>
☹️ 위의 gif를 보면 중간중간에 설정한 로딩 컴포넌트가 보이는 것을 확인할 수 있다. 이는 사용자 경험이 좋지 않다.
왜냐하면 페이지가 캐시에 존재하지 않기 때문에 Next Page 버튼을 누를때마다 그때그때 데이터를 불러와 로딩이 될 수 밖에 없는 것이다.
그렇다면 데이터를 미리 가져와 캐시에 넣어서 버튼을 누를 때마다 바로 출력되게 하면 될 것이다.
✅ 이런 것을 Pre-fetching 이라고 한다.
쉽게 말해 추후에 사용자가 사용할 법한 모든 데이터를 미리 패칭하는 것이다!(공식문서)
prefetch는 queryClient의 메서드이다.
✅ 한가지 주의해야할 점
다음 페이지로 onClick시 실행되는건 좋은 생각이 아니다. 왜냐하면 상태 업데이트가 비동기식으로 일어나기 때문에 이미 업데이트가 진행되었는지 알 방법이 없고 즉, 현재 페이지가 무엇인지 알수 있는 확실한 방법이 없는 것이다.
✅ useEffect를 활용해보자
따라서 useEffect를 사용하여 현재 페이지에 생기는 변경사항을 활용해보자.
의존성배열로 currentPage를 주게 되면, 현재 페이지가 변경될 때마다 함수가 실행 될 것이다.
이때 실행될 함수가 바로 queryClient.prefecthQuery
이다.
1️⃣ 총 10페이지가 max이기 때문에 prefetch는 9페이지까지만 가능
2️⃣ 다음페이지는 currentPage의 + 1
3️⃣ prefetchQuery 역시 useQuery같이 사용
const queryClient = useQueryClient();
useEffect(() => {
if(currentPage < maxPostPage) { // 1️⃣ 번
const nextPage = currentPage + 1; // 2️⃣ 번
queryClient.prefetchQuery( // 3️⃣ 번
['posts', nextPage],
() => fetchPosts(nextPage))
}
},[currentPage, queryClient])
👍 확인해보면 현재 6페이지인데 캐시에 7페이지가 들어와있는 것을 확인할 수 있고, 부드럽게 페이지가 넘어가 사용자 경험도 개선되었다.
💡 isFetching VS isLoading
isFetching의 경우 async 쿼리 함수가 해결되지 않았을 때 True값에 해당한다.
즉, 아직 데이터를 가져오는 중인 것이다.
isLoading은 isFetching이 True값이면서 쿼리에 대해 캐시된 데이터가 없는 상태를 뜻한다.
즉, isLoading이 True인 경우 isFetching 또한 항상 True이다.
만약 아래와 같이 isLoading 이 아닌 isFetching이라면 캐시된 데이터의 존재 여부와 관계 없이 로딩 기능을 동작할 것이다.
if(isFetching) return <div>데이터 가져오는중</div>
따라서, 앞서 진행한 Pre-fetching으로 Loading에 대한 동작은 사라졌었지만, isFetching의 경우에는 캐시와의 관계가 무관하기 때문에 무조건적으로 로딩 기능을 동작하게 된다.
솔직히 우리가 원하는 것은 isLoading 일 것이다. 캐시에 아무 것도 없고 서버에서 데이터를 가져오는 중일때 화면에 보여줄 로딩창이 필요한 것이기 때문이다.