Pagination as a React Component

Pagination as a React Component

Working with databases and API:s, sooner or later you are going to deal with hundreds or more of data entries. A common example of an entry is blog posts. But displaying hundreds of posts on a page is overwhelming! So how to deal with this? This is answered by either pagination or infinite scrolling. Pagination will be the focus for the day as we build something with a similar function to what Google has (pictured below).
image.png 11.8 KB


Pagination is a method of displaying a limited number of results per page. Each page is number (1, 2, 3, etc.). The first page may display the first 10 results, the second displays results 11-20, and so on. It's up to the application designer to specify how ever many results should be displayed per page. The higher number of results per page, the less pages it would take to leaf through all the pages. However, few would be expected to leaf through that many pages. A user would expect to hit what they want on the first page.

Today we will build a pagination using React and React Router. React is a great tool for building a UI, but would more often be better suited for an infinite scrolling solution than a pagination system. The reason for this is that pagination often means hitting a server with a new request by updating the URI with a new page value, while React works more fluid by making an AJAX call and update the current page with the new data. This is where React Router enters the picture. By making a virtual request for a new page, the AJAX call will get its parameters through the URL parameters (as if a GET request is made). While the requests are handled in the same manner behind the scenes an advantage of making a GET request like this is that it will be accessible through the browser history list. This means that it will be easy for a user to retrace their steps if they want to and is the expected behavior.

We will build a pagination component which will display 5 page-links. As the current page is updated the pagination will evaluate which the 5 page-links should be. If on the first page it will have 1, 2, 3, 4, and 5. If on page 10 the links will be 8, 9, 10, 11, and 12. If the first page is not linked, a "back-to-the-start"-link will be displayed too. The gif below pictures this:
pagination-1.gif 158.94 KB


So let's start coding!

Code for Pagination.js
import { Link } from "react-router-dom";

const Pagination = ({resultsPerPage, numberOfResults, baseUrl, currentPage = 1}) => {
    let lastPage = parseInt(numberOfResults/resultsPerPage);
    if (numberOfResults % resultsPerPage > 0) {
        // This is required to display the last couple of results 
        // (e.g. 12 / 10 would leave 2 results to be displayed on the last page)
        lastPage++;
    }

    // Make so that always 5 pages are linked whenever possible.
    const pages = [];
    let limitDown = currentPage - 2;
    limitDown = limitDown < 1 ? 1 : limitDown;
    if (limitDown > lastPage - 5 && lastPage - 5 > 0) {
        limitDown = lastPage - 5;
    }

    let limitUp = currentPage + 2 <= lastPage ? currentPage + 2 : lastPage;
    if (limitUp < 5 && 5 <= lastPage) {
        limitUp = 5;
    }

    if (limitDown > 1) {
        pages.push(<Link key={1} to={baseUrl + 1}>{'|<'}</Link>);
    }

    if (lastPage > 1) {
        for (let i = limitDown; i <= limitUp; i++) {
            pages.push(<Link key={i} to={baseUrl + i}>{i}</Link>);
        }
    }

    return (
        <div style={{display: "flex", gap: "0.5rem"}}>
            {pages}
        </div>
    );
}

export default Pagination;

Before you go ahead an copy this file straight off, consider this: 
  • How many page-links do you want to display at a time?
  • Do you need a "back to the first page"-link?
  • Do you need a "go to the last page"-link? 
  • Do you need to pass all those props to the component or could you set it up in a .env-file instead?
  • How would you configure the baseUrl?
Configure it to suit your needs!

How to use the Pagination component
Say that we make an API-call and find out we have 100 entries to handle. We want to display 10 entries per page. The component takes 4 props to work correctly so how do we fill it out? Also, how do we handle the URL calls? Let's see this in use!

<Pagination 
    resultsPerPage={RESULTS_PER_PAGE} 
    numberOfResults={numOfResults} 
    currentPage={currentPage}
    baseUrl={"/search?q=" + searchQuery + "&p="}
/>

Let's break this down: there are 4 properties to the pagination component. resultsPerPage  and numberOfResults are both used to calculate the maximum number of pages available to paginate. currentPage is used to know where to start pagination from. This would be updated as the user turn the pages. The baseUrl is used to set "href" attribute for the links (or rather the "to" property in React Router Link-component). It's up for the developer to decide what should go in for the baseUrl. In this example we have a query parameter for a search function with a "p" parameter to track the current page.

Extra credits
While this post only covers pagination as a way to display page-links that are updated according to current page number, it leaves out the whole other end of pagination: how to handle the pagination requests being made from the links. There has to be route-listener that handles both the search query from the example above as well as the page parameter. Having both these values, and an understanding of how many results should be displayed per page, the route-listener could fetch the entries needed.

On my GitHub repository book-readers you can see the implementation of both the pagination and the route-listener. It's a work in progress and you can see it evolve on book-readers.netlify.app.