Silver Library (Archived)
Pagination, yet if you need... 본문
Note: this post is a scrapped version of explained pagination. See the link below to read the original text.
Basic concept:
we need as props to our Pagination component:
- totalCount: represents the total count of data available from the source.
- currentPage: represents the current active page. We'll use a 1-based index instead of a traditional 0-based index for our currentPage value.
- pageSize: represents the maximum data that is visible in a single page.
- onPageChange: callback function invoked with the updated page value when the page is changed.
- siblingCount (optional): represents the min number of page buttons to be shown on each side of the current page button. Defaults to 1.
- className (optional): className to be added to the top level container.
From the pagination component we'll invoke the usePagination hook which will take in the following parameters to compute the page ranges: totalCount , currentPage , pageSize , siblingCount .
NOTE: this post uses usePagination hook,
- Our pagination hook must return the range of numbers to be displayed in our pagination component as an array.
- The computation logic needs to re-run when either currentPage, pageSize, siblingCount, or totalCount changes.
- The total number of items returned by the hook should remain constant. This will avoid resizing our pagination component if the length of the range array changes while the user is interacting with the component.
let's create a file called usePagination.js in our project src folder and start with the implementation.
export const usePagination = ({
totalCount,
pageSize,
siblingCount = 1,
currentPage
}) => {
const paginationRange = useMemo(() => {
// Our implementation logic will go here
}, [totalCount, pageSize, siblingCount, currentPage]);
return paginationRange;
};
useMemo hook is to compute core logic. This useMemo calback will run when any value in its dependency array changes.
Set defaultValue of our siblingCount prop to be 1 as it is an optional prop.
And surprisingly, there are types of states of a pagination component. Its four in total.
- Total page count is less than the page pills we want to show. In such a case we just return the range from 1 to totalPageCount.
- Total page count is greater than the page pills but only the right DOTS are visible.
- Total page count is greater than the page pills but only the left DOTS are visible.
- Total page count is greater than the page pills and both the left and the right DOTS are visible.
As a first step, we shall go about calculating the total pages from totalCount and pageSize as follows:
const totalPageCount = Math.ceil(totalCount / pageSize);
Notice that we are using Math.ceil to round of the number to the next higher integer value. This ensures that we are reserving an extra page for the remaining data.
For more:
https://www.freecodecamp.org/news/build-a-custom-pagination-component-in-react/
Once it is done,
- Pagination.js file in our src folder.
Then implement the code logic as follows:
import React from 'react';
import classnames from 'classnames';
import { usePagination, DOTS } from './usePagination';
import './pagination.scss';
const Pagination = props => {
const {
onPageChange,
totalCount,
siblingCount = 1,
currentPage,
pageSize,
className
} = props;
const paginationRange = usePagination({
currentPage,
totalCount,
siblingCount,
pageSize
});
// If there are less than 2 times in pagination range we shall not render the component
if (currentPage === 0 || paginationRange.length < 2) {
return null;
}
const onNext = () => {
onPageChange(currentPage + 1);
};
const onPrevious = () => {
onPageChange(currentPage - 1);
};
let lastPage = paginationRange[paginationRange.length - 1];
return (
<ul
className={classnames('pagination-container', { [className]: className })}
>
{/* Left navigation arrow */}
<li
className={classnames('pagination-item', {
disabled: currentPage === 1
})}
onClick={onPrevious}
>
<div className="arrow left" />
</li>
{paginationRange.map(pageNumber => {
// If the pageItem is a DOT, render the DOTS unicode character
if (pageNumber === DOTS) {
return <li className="pagination-item dots">…</li>;
}
// Render our Page Pills
return (
<li
className={classnames('pagination-item', {
selected: pageNumber === currentPage
})}
onClick={() => onPageChange(pageNumber)}
>
{pageNumber}
</li>
);
})}
{/* Right Navigation arrow */}
<li
className={classnames('pagination-item', {
disabled: currentPage === lastPage
})}
onClick={onNext}
>
<div className="arrow right" />
</li>
</ul>
);
};
export default Pagination;
We render the Pagination component as a list with left and right arrows which handle the previous and next actions the user makes. In between the arrows, we map over the paginationRange and render the page numbers as pagination-items. If the page item is a DOT we render a unicode character for it.
As a special handling we add a disabled class to the left/right arrow if the currentPage is the first or the last page, respectively. We disable the pointer-events and update the styles of the arrow icons through CSS if the icon needs to be disabled.
We also add click event handlers to the page pills which will invoke the onPageChanged callback function with the updated value of currentPage.
What about using custom pagination component?
https://react-1zaeqk.stackblitz.io