React + Spring Boot: Pagination example

In this tutorial, I will show you how to build a full-stack Pagination (React + Spring Boot) example on Server side. The back-end server uses Spring Data and Spring Web for REST APIs, front-end side is a React App with Axios for HTTP Requests.

Related Posts:
React + Spring Boot + MySQL: CRUD example
React + Spring Boot + PostgreSQL: CRUD example
React + Spring Boot + MongoDB: CRUD example
React + Spring Boot: File Upload/Download example
React + Spring Boot: JWT Authentication & Authorization example


Pagination with React & Spring Boot example

Assume that we have tutorials table in database like this:

react-spring-boot-pagination-example-db-table

We need to export APIs for pagination (with/without filter) as following samples:

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

This is structure of the result that we want to get from the APIs:

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

Our React app will display the result with pagination:

react-spring-boot-pagination-example-default-paging

You can change to a page with larger index:

react-spring-boot-pagination-example-change-page

Or change page size (quantity of items per page):

react-spring-boot-pagination-example-change-items-per-page

Or paging with filter:

react-spring-boot-pagination-example-paging-with-filter

Full-stack Architecture

We’re gonna build the Spring Boot + React Pagination application with following architecture:

react-spring-boot-pagination-example-architecture

– Spring Boot exports REST Apis using Spring Web MVC & interacts with Database using Spring Data.
– React Client sends HTTP Requests and retrieve HTTP Responses using axios, shows data on the components. We also use React Router for navigating to pages.

Spring Boot Server side Pagination

Overview

The Spring Boot Server will be built with Spring Web and Spring Data.

It can export API with endpoint that supports both pagination and filter:
api/tutorials?title=[keyword]&page=[index]&size=[number]

The response structure looks like this:

{
  "totalItems": 32,
  "tutorials": [
    {
      "id": 7,
      "title": "bezkoder Tut#7",
      "description": "Tut#6 Description",
      "published": false
    },
    {
      "id": 8,
      "title": "bezkoder Tut#8",
      "description": "Tut#7 Description",
      "published": true
    },
    {
      "id": 9,
      "title": "bezkoder Tut#9",
      "description": "Tut#8 Description",
      "published": true
    }
  ],
  "totalPages": 11,
  "currentPage": 2
}

Project Structure

This is the structure of the project that we’re gonna build:

react-spring-boot-pagination-example-server-side-project-structure

Let me explain it briefly.

Tutorial data model class corresponds to entity/document and table/collection tutorials.
TutorialRepository is an interface for CRUD methods and custom finder methods. It will be autowired in TutorialController.
TutorialController is a RestController which has request mapping methods for RESTful requests such as: getAllTutorials, createTutorial, updateTutorial, deleteTutorial, findByPublished
– Configuration for Spring Datasource, DB Connection in application.properties.
pom.xml contains dependencies for Spring Boot and MySQL/PostgreSQL/MongoDB.

Implementation

Step by step to make the Server side Pagination back-end for this React front-end can be found at one of following posts:
– For MySQL/PostgreSQL: Spring Boot Pagination & Filter example | Spring JPA, Pageable (with Github)
– For MongoDB: Spring Boot MongoDB Pagination example with Spring Data (with Github)

React Pagination Client

Overview

react-spring-boot-pagination-example-client-components-overview

– The App component is a container with React Router. It has navbar that links to routes paths.

TutorialsList component gets and displays Tutorials with pagination and filter.
Tutorial component has form for editing Tutorial’s details based on :id.
AddTutorial component has form for submission new Tutorial.

– These Components call TutorialDataService methods which use axios to make HTTP requests and receive responses.

Project Structure

react-spring-boot-pagination-example-client-project-structure

package.json contains 4 main modules: react, react-router-dom, axios & bootstrap.
App is the container that has Router & navbar.
– There are 3 components: TutorialsList, Tutorial, AddTutorial.
http-common.js initializes axios with HTTP base Url and headers.
TutorialDataService has methods for sending HTTP requests to the Apis.
.env configures port for this React CRUD App.

Setup React.js Application

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

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


public

src

components

add-tutorial.component.js

tutorial.component.js

tutorials-list.component.js

services

tutorial.service.js

App.css

App.js

index.js

package.json


You can follow step by step, or get source code in this post:
React Material UI examples with a CRUD Application

The React Project contains structure that we only need to add some changes (in tutorials-list.component.js and tutorial.service.js) to make the pagination work well.

react-spring-boot-pagination-client-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/tutorial.service.js

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

class TutorialDataService {
  getAll(params) {
    return http.get("/tutorials", { params });
  }

  // other CRUD methods
}

export default new TutorialDataService();

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

Create React Components with Pagination

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-spring-boot-pagination-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/tutorials-list.component.js

import React, { Component } from "react";
import TutorialDataService from "../services/tutorial.service";
...

export default class TutorialsList extends Component {
  constructor(props) {
    super(props);
    this.onChangeSearchTitle = this.onChangeSearchTitle.bind(this);
    this.retrieveTutorials = this.retrieveTutorials.bind(this);
    this.refreshList = this.refreshList.bind(this);
    this.setActiveTutorial = this.setActiveTutorial.bind(this);
    this.removeAllTutorials = this.removeAllTutorials.bind(this);
    this.handlePageChange = this.handlePageChange.bind(this);
    this.handlePageSizeChange = this.handlePageSizeChange.bind(this);

    this.state = {
      tutorials: [],
      currentTutorial: null,
      currentIndex: -1,
      searchTitle: "",

      page: 1,
      count: 0,
      pageSize: 3,
    };

    this.pageSizes = [3, 6, 9];
  }

  componentDidMount() {
    this.retrieveTutorials();
  }

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

    this.setState({
      searchTitle: searchTitle,
    });
  }

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

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

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

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

    return params;
  }

  retrieveTutorials() {
    const { searchTitle, page, pageSize } = this.state;
    const params = this.getRequestParams(searchTitle, page, pageSize);

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

        this.setState({
          tutorials: tutorials,
          count: totalPages,
        });
        console.log(response.data);
      })
      .catch((e) => {
        console.log(e);
      });
  }

  ...

  handlePageChange(event, value) {
    this.setState(
      {
        page: value,
      },
      () => {
        this.retrieveTutorials();
      }
    );
  }

  handlePageSizeChange(event) {
    this.setState(
      {
        pageSize: event.target.value,
        page: 1
      },
      () => {
        this.retrieveTutorials();
      }
    );
  }

  render() {
    ...
  }
}

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() that updates the tutorials List when pagination information changes.

Let’s continue to implement render() method:

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

export default class TutorialsList extends Component {
  ...

  render() {
    const {
      searchTitle,
      tutorials,
      currentTutorial,
      currentIndex,
      page,
      count,
      pageSize,
    } = this.state;

    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={this.onChangeSearchTitle}
            />
            <div className="input-group-append">
              <button
                className="btn btn-outline-secondary"
                type="button"
                onClick={this.retrieveTutorials}
              >
                Search
              </button>
            </div>
          </div>
        </div>
        <div className="col-md-6">
          <h4>Tutorials List</h4>

          <div className="mt-3">
            {"Items per Page: "}
            <select onChange={this.handlePageSizeChange} value={pageSize}>
              {this.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={this.handlePageChange}
            />
          </div>

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

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

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.

React Spring Boot Pagination using Hooks

Step by step to build React Hooks App for the Server side pagination back-end above can be found at:
React Pagination using Hooks example (with Github)

Conclusion

Now we have an overview of full-stack React + Spring Boot Server side Pagination example.

We also take a look at client-server architecture for REST API using Spring Boot with Spring Data, as well as React project structure for building a front-end app to make HTTP requests and consume responses.

Next tutorials show you more details about how to implement the system:
– Back-end (with Github):

– Front-end (with Github):

You may want to know how to run both projects in one place:
How to integrate React.js with Spring Boot

Fullstack CRUD App:
React + Spring Boot + MySQL: CRUD example
React + Spring Boot + PostgreSQL: CRUD example
React + Spring Boot + MongoDB: CRUD example

File Upload: React + Spring Boot: File Upload/Download example

Or Security: React + Spring Boot: JWT Authentication & Authorization example

Happy learning, see you again!

One thought to “React + Spring Boot: Pagination example”

Leave a Reply

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