Applications that display large amounts of data in the form of either a list or table can implement pagination to divide the data into a sequence of pages. Instead of fetching huge amounts of data from a server API, pagination loads data on demand, decreasing load time and improving the overall UI.
While there are many libraries available for implementing pagination, some are a bit heavy, harming your app’s overall performance. In this article, we’ll create a reusable pagination component using React Hooks.
As an example, we’ll display the passenger list in the image below below using pagination. The complete code is available at the react-pagination GitHub repository; please feel free to fork it:
Table of contents
useState
HookuseEffect
Hook- Setting up the project
- Create a pagination component
- Using the pagination component
- Wrapping up
Before jumping into the implementation, let me introduce two React Hooks that we’ll use in this article. If you’re already familiar with React Hooks, feel free to skip to the tutorial.
useState
Hook
If you’d like to add state to your functional component, the useState
Hook will do the job. Consider the example below:
// a new state variable called "currentPage" const [currentPage, setCurrentPage] = useState(1);
The useState
Hook helps to preserve the values between function calls. It takes a single argument, which is the initial state. In the example above, the currentPage
variable is set to 1
by default. The useState
Hook will return a pair of values, the first being the variable’s state, and the second is the function to set the state.
We’ll use the useState
Hook to maintain the state for the current page, the minimum and maximum page limits, as well as hold the response data from the passenger API.
useEffect
Hook
If you’d like to perform any side effects like data fetching, DOM manipulation, or subscribing and unsubscribing to events, the useEffect
Hook will help you:
useEffect(()=>{ // call rest api },[]);
React has access to the state of your component, so it will essentially remember the function you passed and invoke it after DOM updates. We use the useEffect
Hook to fetch passenger data from the REST API.
Setting up the project
Our goal is to display the passenger list with pagination, so we’ll use the free REST API to provide a list of passenger data. Begin by creating a new React application using the Create React App command below:
npx create-react-app pagination-tutorial
Remove all the default content from the App.js
file. We’ll include our components here later. The final file should look like the code below:
import './App.css'; function App() { return ( <div className="App"> <div className="App-header"> // include the Passenger componnet, which we create by end of the article </div> </div> ); } export default App;
Now, we can run the application with the command below:
yarn start
Create a pagination component
The functional component allows you to split the UI into independent and reusable pieces. We’ll use a functional component that has a single argument holding props to the component. Before creating our pagination component, let’s clarify a few of the few terms used throughout the rest of the article.
- Current page: represents the user position on the pager
- Max page limit: represents the upper limit on the pager
- Min page limit: represents the lower limit on the pager
Jump to the src
folder and create a new file called Pagination.js
:
import React from 'react'; const Pagination = (props)=>{ return( <div> </div> ) } export default Pagination;
Since we’re creating a reusable component for pagination, assume that the component gets all inputs from the parent component using props. We aim to build an output like the screenshot below:
Initialize the terms we discussed in previous section:
// init const { currentPage, maxPageLimit, minPageLimit} = props; const totalPages = props.response.totalPages-1; const data = props.response.data;
Now, to input the total number of pages, we’ll build page numbers starting from 1
. The limit will go based on maximum and minimum page limits:
Build the pages array, which will hold all the numbers ranging from 1
to total pages:
// build page numbers list based on total number of pages const pages = []; for(let i=1 ; i<=totalPages; i++){ pages.push(i); }
To create the UI for all page numbers that fall within the maximum and minimum page limits, iterate through the pages array built with the step above. If the iteration page matches with the current page, make it active:
const pageNumbers = pages.map(page => { if(page <= maxPageLimit && page > minPageLimit) { return( <li key={page} id={page} onClick={handlePageClick} className={currentPage===page ? 'active' : null}> {page} </li> ); }else{ return null; } } );
Next, we’ll create ellipsis dots …
, which appear when there are more pages than the current page on either the left or right. On clicking the ellipsis, the user can move to either the previous or the next group of pages. You can see these in the image below, highlighted in green:
// page ellipses let pageIncrementEllipses = null; if(pages.length > maxPageLimit){ pageIncrementEllipses = <li onClick={handleNextClick}>…</li> } let pageDecremenEllipses = null; if(minPageLimit >=1){ pageDecremenEllipses = <li onClick={handlePrevClick}>…</li> } const renderData = (data)=>{
Let’s render our passengers in a list format. In the code below, I’m showing the ID of the passenger and the name of the airline used by each passenger:
return( <ul> {data.map((d)=> <li key={d['_id']}> The passenger having id {d['_id'].slice(d['_id'].length-5)} using {d.airline[0].name} airlines</li>) } </ul> ) }
Next, we’ll combine all of the UI elements and return them in our functional component along with the Prev and Next buttons.
On clicking Prev, the user navigates to the previous page. On clicking Next, the user navigates to the next page. The UI elements are aligned as follows:
1. Passenger’s data 2. Pager 1. Prev button 2. Page decrement ellipses 3. Page numbers with current page selection 4. Page increment ellipses 5. Next button return ( <div className="main"> <div className="mainData"> {renderData(data)} </div> <ul className="pageNumbers"> <li> <button onClick={handlePrevClick} disabled={currentPage === pages[0]}>Prev</button> </li> {pageDecremenEllipses} {pageNumbers} {pageIncrementEllipses} <li> <button onClick={handleNextClick} disabled={currentPage === pages[pages.length-1]}>Next</button> </li> </ul> </div> )
So far, we created the UI, which includes events like page click, prev click, and next click. Now, let’s bind all the events:
const handlePrevClick = ()=>{ props.onPrevClick(); } const handleNextClick = ()=>{ props.onNextClick(); } const handlePageClick = (e)=>{ props.onPageChange(Number(e.target.id)); }
Using the pagination component
Now, we’ll navigate to the parent component, which is the Passengers
component in our case, responsible for fetching passenger data and setting the pagination props. Use the Pagination
component created above in this component.
To create the Passengers
component, first, initialize the state. You can refer to the state variables below. In simple terms, we’ll show page numbers starting from 1
to 5
at the start of our pagination component. We initialize the minimum and maximum page limits with 0
and 5
, respectively.
We set the current page to 1
, meaning that the user will be on the first page. By setting the passenger data to an empty array, we determine that we’ll fetch the data in the next steps. The page number limit, 5
, tells us that we’ll show only five page numbers on UI:
const pageNumberLimit = 5; const [passengersData, setData] = useState([]); const [loading, setLoading] = useState(true); const [currentPage, setCurrentPage] = useState(1); const [maxPageLimit, setMaxPageLimit] = useState(5); const [minPageLimit, setMinPageLimit] = useState(0);
When the current page changes, we’ll fetch the passenger data from our API with the useEffect
Hook, persisting it in the passengersData
state variable:
useEffect(()=>{ setLoading(true); fetch(`https://api.instantwebtools.net/v1/passenger?currentPage=${currentPage}&size=5`) .then((response) => response.json()) .then((json) => { setData(json); setLoading(false);}); },[currentPage]);
The core step of this complete pagination is updating the maximum, minimum, and current page, which are updated on the page change, previous click, and next click.
Previous click
Assume you are on the sixth page of our pagination component. If you click the Prev button, you’ll be directed to the fifth page, and the page limits should be reset to 1
and 5
.
To achieve the example above, we’ll update the maximum and minimum page limits only if the previous page is not in the current page range. The remainder obtained after dividing the previous page with page number should be zero.
For example, consider that the maximum page limit should equal the current max limit minus the page number limit. In our example in the picture above, 10 minus five equals five.
For the minimum page limit, we subtract the page number limit from the current minimum limit. In our example, we calculate that six minus five equals one. Keep in mind that the current page is the previous page minus 1
.
Next click
We’ll explore the next click as a toggle of the previous example. Assume you’re on the fifth page now. Click Next, and you should navigate to the sixth page. The page limits should be between 6
and 10
.
Like with the previous click, we can only achieve the scenario above by updating the maximum and minimum page limits. Maximum and minimum page limits are updated only when the next page is greater than the current max page limit:
To calculate the maximum page limit, we add the current maximum limit to the page number limit. In our example, we add five and five to equal 10.
The minimum page limit is the current minimum limit plus the page number limit. In our example, one plus five equals six. The current page is the previous page plus one.
Page change
On click of any page number, update the current page to the specific number. Below is the code snippet for all major events:
const onPageChange= (pageNumber)=>{ setCurrentPage(pageNumber); } const onPrevClick = ()=>{ if((currentPage-1) % pageNumberLimit === 0){ setMaxPageLimit(maxPageLimit - pageNumberLimit); setMinPageLimit(minPageLimit - pageNumberLimit); } setCurrentPage(prev=> prev-1); } const onNextClick = ()=>{ if(currentPage+1 > maxPageLimit){ setMaxPageLimit(maxPageLimit + pageNumberLimit); setMinPageLimit(minPageLimit + pageNumberLimit); } setCurrentPage(prev=>prev+1); }
To create the UI for passengers with pagination, we’ll combine all the attributes needed and send them as props to the pagination component:
const paginationAttributes = { currentPage, maxPageLimit, minPageLimit, response: passengersData, }; return( <div> <h2>Passenger List</h2> {!loading ? <Pagination {...paginationAttributes} onPrevClick={onPrevClick} onNextClick={onNextClick} onPageChange={onPageChange}/> : <div> Loading... </div> } </div> )
Now, we’ve built both parent and child components. You are ready to use the parent component anywhere in the application.
Wrapping up
In this tutorial, we created a pagination component by sending the current page, maximum page limit, minimum page limit, and other page events as props to the component. We used React Hooks for state management and fetching data.
While we developed our example application in React, you can apply the same logic to any framework. I hope you learned something new! Be sure to leave a comment if you have any questions. Happy coding!
The post React pagination from scratch using Hooks appeared first on LogRocket Blog.
from LogRocket Blog https://ift.tt/2DLluW1
via Read more