How to build a pagination component with React-Query

React-Query has recently emerged as one of the best tools a React developer could have in their arsenal. Gone are the days when you had nested functions to make API calls from components to fetch data from the server, handle the timing of those calls with the help of side effects (the useEffect hook) and write long and verbose redux actions and reducers just so the value could be stored in a global state (yes, React-Query can replace Redux).

If this was not enough to impress you, React-Query does all of these things with just a few lines of code.

To let you focus more on developing your product rather than engaging in the repetitive parts of software development, it takes care of the following for you.

  1. Fetching the data from the server when required.
  2. Caching the data so that your web apps are performant and super fast.
  3. Storing your API results in a global state (bye-bye Redux).
  4. Makes handling the side effects of an API call a breeze (the success and failure of it).

If you are not aware of how you can do that, don’t worry, you will see all of these points in action here today.

Now that I have got you excited about React-Query, let’s get started!

What we will build

Paginated list of users
Paginated List of Users

We will be building a list of paginated users. We will use this open to all API “https://reqres.in/api/users?page=1” which will provide us the list of users according to the page number we have requested.

Prerequisites:

Basic knowledge of React.

Things we will cover in this tutorial

  1. Basic Setup & Components
  2. Setup React-Query and React-Query Devtools
  3. Use React-Query to fetch our users from the API
  4. Pagination

Let’s get started!

I have a Github repository for you in case you want to skip the “Basic Setup and Components” part and jump right into action to the React-Query part.

https://github.com/upmostly/react-query-pagination

You can clone this repository in case you want to skip Part 1 of this tutorial.

After the Basic Setup your project will look something like this.

UI Setup

Part 1: Basic Setup & Components

Step 1: Initialize a react repository with the name “react-query-pagination”.

npx create-react-app react-query-pagination

Step 2: Create “TableHeader” Component

Create a src/components/tableHeader folder.

Inside that folder create two files namely TableHeader.jsx and TableHeader.css

src/components/tableHeader/TableHeader.jsx

import './TableHeader.css';

const TableHeader = () => {
 return (
   <div className='table-header' >
     <span>Name</span>
     <span>Email</span>
   </div>
 );
};

export default TableHeader;

src/components/tableHeader/TableHeader.css

body {
 background-color: #e7eff8;
}

.table-header {
 background-color: #f5f7fb;
 height: 50px;
 width: 60%;
 margin-left: 50%;
 transform: translateX(-50%);
 margin-top: 50px;
 border-radius: 5px;
 border: 1px solid #ecf2f7;
 display: flex;
 align-items: center;
}

.table-header > span {
 width: 50%;
 padding-left: 5%;
}

Step 3: Create a “TableRow” Component

Create a src/components/tableRow folder.

Inside that folder create two files namely TableRow.jsx and TableRow.css

src/components/tableRow/TableRow.jsx

import "./TableRow.css";

const TableRow = () => {
 return (
   <div className="table-row">
     <span>
       <img
         src="https://randomuser.me/api/portraits/men/88.jpg"
         alt="avatar"
       />
       <span>Lukas Graham</span>
     </span>
     <span>emailid@gmail.com</span>
   </div>
 );
};

export default TableRow;

src/components/tableRow/TableRow.css

.table-row {
 background-color: white;
 width: 60%;
 margin-left: 50%;
 transform: translateX(-50%);
 margin-top: 10px;
 border-radius: 5px;
 display: flex;
 align-items: center;
}

.table-row > span {
 display: flex;
 align-items: center;
 width: 50%;
 padding-left: 5%;
 padding-top: 10px;
 padding-bottom: 10px;
}

.table-row > span > img {
 width: 50px;
 height: 50px;
 border-radius: 50%;
 margin-right: 10px;
}

Step 4: Create a “Pagination” Component

Create a src/components/pagination folder.

Let’s create a CSS file first in this case. So, we can talk about the functionality a bit on this component next.

src/components/pagination/Pagination.css


.pagination {
 display: flex;
 justify-content: center;
 margin-top: 40px;
}

.pagination div {
 width: 33px;
 height: 33px;
 background: white;
 box-shadow: 0px 0px 4px rgba(0, 0, 0, 0.15);
 border-radius: 50%;
 font-size: 15px;
 display: flex;
 justify-content: center;
 align-items: center;
 color: #bdbdbd;
 margin-right: 20px;
 cursor: pointer;
}

.pagination div.active {
 background-color: #0555fe;
 color: white;
}

.pagination div.pagination-arrow {
 font-size: 24px;
 color: #91b4f9;
}

.pagination div.pagination-arrow.inactive {
 opacity: 0.7;
 cursor: not-allowed;
}

Now, coming to the “Pagination” JSX component.

src/components/pagination/Pagination.jsx

import React from "react";
import PropTypes from "prop-types";
import "./Pagination.css";

const Pagination = ({ activePage, pages, setActivePage }) => {
  return (
   <div className="pagination">
     <div
       // Previous page (<) inactive if current page is 1
       className={`pagination-arrow ${activePage === 1 ? "inactive" : ""}`}
       onClick={() => activePage !== 1 && setActivePage((page) => page - 1)}
     >
       {"<"}
     </div>

     {getPages()} {/* We will handle this method in the next step */}
    
     <div
       // Next Page (>) inactive if the current page is the last page.
       className={`pagination-arrow ${activePage === pages ? "inactive" : ""}`}
       onClick={() =>
         activePage !== pages && setActivePage((page) => page + 1)
       }
     >
       {">"}{" "}
     </div>
   </div>
 );
};

// activePage, setActivePage and pages will be passed down as props

Pagination.propTypes = {
 activePage: PropTypes.number,
 pages: PropTypes.number,
 setActivePage: PropTypes.func,
};

export default Pagination;

Now we will add the “getPages” function to the Pagination Component.

 // Methods
 const getPages = () => {
   const elements = [];
   for (let i = 1; i <= pages; i++) {
     elements.push(
       <div
         className={`${activePage === i ? "active" : ""}`}
         onClick={() => setActivePage(i)}
         key={i}
       >
         {i < 10 ? `0${i}` : i}
       </div>
     );
   }
   return elements; // [<div>1</div>, <div>2</div>....]
 };

Step 5: Call all these Components in the “App.js” file.

src/App.js

import { useState } from "react";
import TableHeader from "./components/tableHeader/TableHeader";
import TableRow from "./components/tableRow/TableRow";
import Pagination from "./components/pagination/Pagination";

const App = () => {
 // States
 const [activePage, setActivePage] = useState(1);

 return (
   <>
     <TableHeader />
     <TableRow />
     <Pagination
       activePage={activePage}
       setActivePage={setActivePage}
       pages={2} // Total number of pages
     />
   </>
 );
};

export default App;

We have the basic components now so our list looks something like this.

Basic list UI
Basic Components without functionality

Part 2: Setup React Query and React Query Devtools

You can skip this part in case you are already familiar with the setup of React-Query

Step 1: install react-query and axios (data fetching library)

npm install react-query axios

Step 2: Setup React-Query

In your src/index.js file

import React from "react";
import ReactDOM from "react-dom";
// React Query Essentials
import { QueryClient, QueryClientProvider } from "react-query";

// Devtools to monitor our data fetched
import { ReactQueryDevtools } from "react-query/devtools";
import App from "./App";

// React Query Config
const queryClient = new QueryClient(); // Global Store Instance

ReactDOM.render(
 // Provide access to Global Store
 <QueryClientProvider client={queryClient}>
   <React.StrictMode>
     <App />
   </React.StrictMode>

   {/* DevTools Setup */}
   <ReactQueryDevtools initialIsOpen={false} />
 </QueryClientProvider>,
 document.getElementById("root")
);

We can see that our devtools have been setup when this nice little logo appears at the bottom left of our page. Clicking on it however would just reveal a blank canvas as we don’t have any data fetched through the library.

React Query Devtools
React-Query Devtools

Part 3: Use React-Query to fetch our users from the API.

Step 1: Create a “useUsers” hook to fetch our users from the api.

Create a src/hooks/users.js file. Add the following lines of code in the file.

src/hooks/users.js

import { useQuery } from "react-query";
import axios from "axios";

export const useUsers = () => {
 return useQuery("users", async () => {
   const { data } = await axios.get("https://reqres.in/api/users?page=1");

   return data;
 });
};

Step 2: Import this hook in “App.js”.

src/App.js

import { useState } from "react";
import TableHeader from "./components/tableHeader/TableHeader";
import TableRow from "./components/tableRow/TableRow";
import Pagination from "./components/pagination/Pagination";
// Import the hook
import { useUsers } from "./hooks/users";

const App = () => {
 const [activePage, setActivePage] = useState(1);

 // React Query takes care of calling
 // userUsers hook when App.js is rendered.
 const usersList = useUsers();

 return (
   <>
     <TableHeader />
     {/* map through users list */}
     {usersList.data &&
       usersList.data.data.map((user) => <TableRow user={user} />)}
     <Pagination
       activePage={activePage}
       setActivePage={setActivePage}
       pages={2}
     />
   </>
 );
};

export default App;

We can see the fetched users in the devtools.

Users fetched using react-query
Users fetched using React-Query

Step 4: Connect the “TableRow” component to our data fetched

In case you are not familiar with React’s state management and props, check out this beginner’s tutorial which will help you in this.

After completing Step 4, we now have our list of users without the pagination. It should be looking something like this.

Part 4: Pagination

Now we will add Pagination to the component by changing  just 4 lines of code.

Step 1. Pass “activePage” state to our useUsers hooks

In src/App.js, change the “usersList” variable to:-

const usersList = useUsers(activePage);

Step 2: Change the “useUsers” hook to allow Pagination

// Accept activePage as a parameter from App.js
export const useUsers = (activePage) => {
 return useQuery(
   // Add activePage as a dependency
   ["users", activePage],
   async () => {
     const { data } = await axios.get(
       // Change page number to the activePage param received
       `https://reqres.in/api/users?page=${activePage}`
     );

     return data;
   },
   // This tells React-Query that this is Query is part of
   // a paginated component
   { keepPreviousData: true }
 );
};

Let’s take a look at 2 lines in particular to see why we did that

useQuery(["users", activePage], () => {})

We changed the query from a string to an array to accommodate “activePage” as a dependency.

Doing so, will tell React-Query to call the “users” query again in case the “activePage” is changed (similar to useEffect hook right?).

{ keepPreviousData: true }

We added this line in our useQuery hook in order to tell React-Query that this query is part of a paginated component. React-Query would then take care to not remove the cached data for each page for a super fast response time in case of a page change.

Enough said, let’s review how these changes will get reflected in our application as well as the devtools.

Paginated list of users
The

We can see that the Pagination is working flawlessly. The load times are super quick as well because of caching.

Let’s see how the our global state has changed in the devtools.

Cached data in react-query
Cached data for each page

We can see that our data is stored in two keys now, namely:-

  1. “[‘users’, 1]” for the data of users for the first page.
  2. “[‘users’, 2]” for the data of users for the second page.

Voila! Now you know how to build a pagination component with React-Query. Our web-app looks super fast with a lot less lines of code than usual, thanks to React-Query.

Let us know in the comments if you found this helpful so that we remain motivated to keep making such interesting and useful content.

👋 Hey, I'm Tarun Pathak
Tarun is a professional frontend developer with a passion for React JS. He's dedicated his career to making developers' learning experience smoother through clearly explained advanced concepts.

💬 Leave a comment

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

We will never share your email with anyone else.