대량의 데이터를 여러 페이지로 나누어 보여주는 기법
페이지당 항목 수(Items per Page): 한 페이지에 표시되는 데이터 항목의 수
총 데이터 개수(Total Count): 전체 데이터의 총 개수
전체 페이지 수(Total Pages): 전체 데이터를 페이지당 항목 수로 나누어 얻은 총 페이지 수
페이지 번호(Page Number): 현재 페이지의 번호
사용자가 번호 클릭함에 따라 변경될 수 있는 값이므로 상태로 관리
다음/이전 페이지(Next/Previous Page): 사용자가 다음 또는 이전 페이지로 이동할 수 있도록 하는 것
export default function Pagination({ totalPages, page, setPage }) {
return (
<div
style={{ display: "flex", justifyContent: "center", marginTop: "20px" }}
>
<button
onClick={() => setPage((prev) => Math.max(prev - 1, 1))}
disabled={page === 1}
>
prev
</button>
{[...Array(totalPages)].map((_, index) => (
<button
key={index + 1}
onClick={() => setPage(index + 1)}
style={{
fontWeight: page === index + 1 ? "bold" : "normal",
margin: "0 5px",
}}
>
{index + 1}
</button>
))}
<button
onClick={() => setPage((prev) => Math.min(prev + 1, totalPages))}
disabled={page === totalPages}
>
next
</button>
</div>
);
}
사용자 인터랙션에 따른 추가 데이터 로드
하는 UI useInfiniteQuery
를 이용
useInfiniteQuery 공식 문서
pages
queryFn 요청 시 마다 리턴값을 배열안에 누적합니다.
pageParams
queryFn 요청 시 매개변수로 넘겨준 pageParam 값을 배열안에 누적합니다.
hasNextPage
추가로 불러온 다음 페이지 있는 지 여부
getNextPageParam
함수의 리턴값이 undefined
인 경우false
다음페이지가 있을 경우에만 더보기 버튼이 노출
{hasNextPage && (
<button onClick={() => fetchNextPage()} disabled={isFetchingNextPage}>
{isFetchingNextPage ? "로딩중..." : "더보기"}
</button>
)}
요청하는 데이터의 총 데이터 개수
, 페이지 당 개수
만 알아도 다음 페이지 존재 여부를 알 수 있다.
// lastPage: 가장 최근에 받아온 페이지 데이터
// allPages: 현재까지 누적된 전체 페이지 데이터 (배열)
getNextPageParam: (lastPage, allPages, lastPageParam) => {
const nextPage = lastPageParam + 1;
return nextPage <= lastPage.totalPages ? nextPage : undefined;
},
intersection observer
가 보편적이지만, 리액트에서는 react-intersection-observer
패키지의 useInView
훅을 이용하는 것이 좋다.
const { ref } = useInView({
threshold: 1,
onChange: (inView) => {
if (inView && hasNextPage && !isFetchingNextPage) {
fetchNextPage();
}
},
});
return (
<>
<ul style={{ listStyle: "none", width: 250, backgroundColor: "beige" }}>
{todos.map((todo, idx) => {
const isLastItem = todos.length - 1 === idx;
return (
<TodoItem ref={isLastItem ? ref : null} key={todo.id} todo={todo} />
);
})}
</ul>
</>
);