react-pagination-hooks-material-ui-feature-image

React Pagination using Hooks example

In this tutorial, I will show you how to make Pagination in a React Hooks Application with existing API using Material-UI.

Related Posts:
React Hooks CRUD example with Axios and Web API
React Hooks: JWT Authentication (without Redux) example
React Hooks File Upload example with Axios & Progress Bar


React Hooks Pagination overview

One of the most important things to make a website friendly is the response time, and pagination comes for this reason. For example, this bezkoder.com website has hundreds of tutorials, and we don’t want to see all of them at once. Paging means displaying a small number of all, by a page.

Assume that we have tutorials table in database like this:

react-pagination-hooks-material-ui-db-table

Our React.js app will display the result with pagination:

react-pagination-hooks-material-ui-default-paging

Change to a page with larger index:

react-pagination-hooks-material-ui-change-page

We can change quantity of items per page:

react-pagination-hooks-material-ui-change-items-per-page

Or paging with filter:

react-pagination-hooks-material-ui-paging-with-filter

The API for this React client can be found at one of following posts:
Node.js Express Pagination with MySQL
Node.js Express Pagination with PostgreSQL
Node.js Express Pagination with MongoDB
Spring Boot Pagination & Filter example | Spring JPA, Pageable
Spring Boot MongoDB Pagination example with Spring Data

These Servers will exports API for pagination (with/without filter), here are some url samples:

  • /api/tutorials?page=1&size=5
  • /api/tutorials?size=5: using default value for page
  • /api/tutorials?page=1: using default value for size
  • /api/tutorials?title=data&page=1&size=3: pagination & filter by title containing ‘data’

This is structure of the response for the HTTP GET request:

{
    "totalItems": 8,
    "tutorials": [...],
    "totalPages": 3,
    "currentPage": 1
}

We actually only need to use tutorials and totalPages when working with Material-UI.

React Hooks Pagination using Material-UI

Material-UI provides Pagination component that enables the user to select a specific page from a range of pages.

For example, with Pagination components below:

<Pagination count={10} />
<Pagination count={10} color="primary" />
<Pagination count={10} variant="outlined" />
<Pagination count={10} variant="outlined" color="primary" />
<Pagination count={10} shape="rounded" />
<Pagination count={10} variant="outlined" shape="rounded" />

We’re gonna have UI like this-

react-pagination-with-api-material-ui-pagination-components

There are also 2 optional props for Ranges:

  • siblingRangespecify: how many digits to display either side of current page
  • boundaryRange: adjacent to the start and end page number

For example:

<Pagination count={11} defaultPage={6} siblingCount={0} />
<Pagination count={11} defaultPage={6} /> {/* Default ranges */}
<Pagination count={11} defaultPage={6} siblingCount={0} boundaryCount={2} />
<Pagination count={11} defaultPage={6} boundaryCount={2} />

react-pagination-with-api-material-ui-pagination-components-ranges

For handling page changes, we use onChange:

export default function PaginationControlled() {
  const [page, setPage] = React.useState(1);
  const handleChange = (event, value) => {
    setPage(value);
  };

  return (
    <div>
      <Pagination count={10} page={page} onChange={handleChange} />
    </div>
  );
}

Notice that count is totalPages in the API response, and page is the current page.

Technology

  • React 16
  • axios 0.19.2
  • material-ui 4

Setup React Application

Open cmd at the folder you want to save Project folder, run command:
npx create-react-app react-pagination-hooks

After the process is done. We create additional folders and files like the following tree:


public

src

components

AddTutorial.js

Tutorial.js

TutorialsList.js

services

TutorialService.js

App.css

App.js

index.js

package.json


You can follow step by step, or get source code in this post:
React Hooks CRUD example with Axios and Web API

The React Project contains structure that we only need to add some changes (in TutorialsList.js and TutorialService.js) to make the pagination work well.

react-pagination-hooks-material-ui-project-structure

Or you can get the new Github source code at the end of this tutorial.

Setup Material-UI for React Pagination App

We need to install both Material-UI core and lab with command:
npm install @material-ui/core @material-ui/lab

Initialize Axios for React HTTP Client

Let’s install axios with command: npm install axios.
Under src folder, we create http-common.js file with following code:

import axios from "axios";

export default axios.create({
  baseURL: "http://localhost:8080/api",
  headers: {
    "Content-type": "application/json"
  }
});

Create Data Service

In this step, we’re gonna create a service that uses axios object above to send HTTP requests.

services/TutorialService.js

import http from "../http-common";

const getAll = (params) => {
  return http.get("/tutorials", { params });
};

// other CRUD methods

export default {
  getAll,
  ...
};

In the code above, you can see that we pass params object to GET method.
The params object will have one, two or all of the fields: title, page, size.

Create React Pagination Component with Hooks

This component has:

  • a search bar for finding Tutorials by title
  • a select element for quantity of items per page
  • a Material-UI Pagination component
  • a tutorials array displayed as a list on the left
  • a selected Tutorial which is shown on the right

react-pagination-hooks-material-ui-default-paging

So we will have following state:
– search and display Tutorials:

  • searchTitle
  • tutorials
  • currentTutorial and currentIndex

– pagination:

  • page: current page
  • count: total pages
  • pageSize: number of items in each page

For pagination, we need to use TutorialDataService.getAll() methods.

components/TutorialsList.js

import React, { useState, useEffect } from "react";
import TutorialDataService from "../services/TutorialService";
...

const TutorialsList = () => {
  const [tutorials, setTutorials] = useState([]);
  const [currentTutorial, setCurrentTutorial] = useState(null);
  const [currentIndex, setCurrentIndex] = useState(-1);
  const [searchTitle, setSearchTitle] = useState("");

  const [page, setPage] = useState(1);
  const [count, setCount] = useState(0);
  const [pageSize, setPageSize] = useState(3);

  const pageSizes = [3, 6, 9];

  const onChangeSearchTitle = (e) => {
    const searchTitle = e.target.value;
    setSearchTitle(searchTitle);
  };

  const getRequestParams = (searchTitle, page, pageSize) => {
    let params = {};

    if (searchTitle) {
      params["title"] = searchTitle;
    }

    if (page) {
      params["page"] = page - 1;
    }

    if (pageSize) {
      params["size"] = pageSize;
    }

    return params;
  };

  const retrieveTutorials = () => {
    const params = getRequestParams(searchTitle, page, pageSize);

    TutorialDataService.getAll(params)
      .then((response) => {
        const { tutorials, totalPages } = response.data;

        setTutorials(tutorials);
        setCount(totalPages);

        console.log(response.data);
      })
      .catch((e) => {
        console.log(e);
      });
  };

  useEffect(retrieveTutorials, [page, pageSize]);

  ...

  const handlePageChange = (event, value) => {
    setPage(value);
  };

  const handlePageSizeChange = (event) => {
    setPageSize(event.target.value);
    setPage(1);
  };

  return (
    ...
  );
};

export default TutorialsList;

Let me explain some lines of code.

In the retrieveTutorials() method:
– We get searchTitle, page, pageSize state and transform them into params object:

{
    "title": searchTitle,
    "page": page - 1,
    "size": pageSize
}

– We use tutorials and totalPages as count state from the response data:

{
    "totalItems": 8,
    "tutorials": [...],
    "totalPages": 3,
    "currentPage": 1
}

handlePageChange() and handlePageSizeChange() methods are for setting new page and pageSize with callback invoking retrieveTutorials() method that updates the tutorials List when pagination information changes.

Let’s continue to implement render() method:

...
import Pagination from "@material-ui/lab/Pagination";

const TutorialsList = () => {
  ...

  return (
    <div className="list row">
      <div className="col-md-8">
        <div className="input-group mb-3">
          <input
            type="text"
            className="form-control"
            placeholder="Search by title"
            value={searchTitle}
            onChange={onChangeSearchTitle}
          />
          <div className="input-group-append">
            <button
              className="btn btn-outline-secondary"
              type="button"
              onClick={retrieveTutorials}
            >
              Search
            </button>
          </div>
        </div>
      </div>
      <div className="col-md-6">
        <h4>Tutorials List</h4>

        <div className="mt-3">
          {"Items per Page: "}
          <select onChange={handlePageSizeChange} value={pageSize}>
            {pageSizes.map((size) => (
              <option key={size} value={size}>
                {size}
              </option>
            ))}
          </select>

          <Pagination
            className="my-3"
            count={count}
            page={page}
            siblingCount={1}
            boundaryCount={1}
            variant="outlined"
            shape="rounded"
            onChange={handlePageChange}
          />
        </div>

        <ul className="list-group">
          {tutorials &&
            tutorials.map((tutorial, index) => (
              <li
                className={
                  "list-group-item " + (index === currentIndex ? "active" : "")
                }
                onClick={() => setActiveTutorial(tutorial, index)}
                key={index}
              >
                {tutorial.title}
              </li>
            ))}
        </ul>

      </div>
      
      ...
    </div>
  );
};

export default TutorialsList;

Configure Port for Web API

Because most of HTTP Server use CORS configuration that accepts resource sharing retricted to some sites or ports, so we also need to configure port for our App.

In project folder, create .env file with following content:

PORT=8081

Now we’ve set our app running at port 8081.

Run React Pagination App

First you need to run the Server at one of following posts:

Then you can run our App with command: npm start.
If the process is successful, open Browser with Url: http://localhost:8081/ and check it.

Conclusion

Today we’ve built a React Pagination Hooks app that consumes API successfully with Material UI. I hope you apply it in your project at ease.

If you want to use React Components instead of Hooks, please visit:
React Pagination with API using Material-UI

Happy learning, see you again!

Further Reading

Source Code

You can find the complete source code for this tutorial on Github.

2 thoughts to “React Pagination using Hooks example”

  1. This tutor is just awesome. Here is my issue:-
    I am made the required changes by followed by last hooks crud tutorial. I am not able to view the list of tutorial on tutorials
    page.
    I made the local aJSON file like this :-
    {
    “tutorials”: [{ “title”: “test”,
    “description”: “description”
    }]
    }

    I am able to submit data but not able to view.

    1. Hi, you should use the new Server-side pagination that I mentioned in the tutorial. It updates some code in the controller to return pagination data 🙂

Leave a Reply

Your email address will not be published. Required fields are marked *