In this article, we will implement a generic, reusable pagination mechanism. We will achieve this by implementing our own custom hook.
What is a Custom Hook?
To quote the official React documentation:
A custom Hook is a JavaScript function whose name starts with "use" and that may call other Hooks.
In essence, a custom hook is a React functional component which exports props where at least one prop is prefixed with the keyword use
. The use
prop acts like a constructor: It defines the initial value that the functional component needs. Other props can be functions or state values of your hook - you decide what you want to expose.
Requirements
Pagination is about dividing a list of items into different "pages". It allows users to browse between the pages, either by going to the next, the previous or to jump to any page inside this collection. Therefore, we identify the following requirements:
- should allow to jump to the previous or the next page
- should allow to jump to any page in the collection
- should return the data that corresponds to the current page
For the pagination custom hook, this means these requirements:
- should be configurable about the number of items per page
- should be stateful to keep the current page
Step 1: Configure Number of Items & Current Page
Let’s start the implementation of the custom hook. First, we define the usePagination
constructor. It receives the data and the number of items per page. We also implement a stateful variable currentPage
.
1 import React, {useState} from 'react';
2
3 function usePagination(data, itemsPerPage) {
4 const [currentPage, setCurrentPage] = useState(1);
5 const maxPage = Math.ceil(data.length / itemsPerPage);
6 }
7
8 export default usePagination;
In more detail:
- In line 3, we implement the
usePagination
function, passing the parametersdata
anditemsPerPage
- In line 4, we define the
currentPage
state variable by using theuseState
built-in hook 1 - In line 5, we set the
maxPage
variable, which defines the upper limit of the number of pages that we can show - In line 8, we export the
usePagination
function
Step 2: Increase, Decrease and Go to any Page
These requirements provide the core features of our hook. We will implement them as functions and export them. Thereby, we need to ensure that the value of currentPage
always lies within the range of 1 and the maxPage
value.
Let’s code:
1 function next() {
2 setCurrentPage((currentPage) => Math.min(currentPage + 1, maxPage));
3 }
4
5 function prev() {
6 setCurrentPage((currentPage) => Math.max(currentPage - 1, 1));
7 }
8
9 function jump(page) {
10 const pageNumber = Math.max(1, page)
11 setCurrentPage((currentPage) => Math.min(pageNumber, maxPage));
12 }
- Line 1: The
next
function increases thecurrentPage
by 1, but does not exceedmaxPage
- Line 5: The
prev
function decreases thecurrentPage
by 1, but does not go below 1 - Line 9: The
jump
function takes care thatcurrentPage
stays within both limits
Step 3: Returning Data of the Current Page
The last step is to implement that only data of the current page is shown.
1 function currentData() {
2 const begin = (currentPage - 1) * itemsPerPage;
3 const end = begin + itemsPerPage;
4 return data.slice(begin, end);
5 }
The variable data
hold all items of the pagination component. From this, we select exactly itemsPerPage
, beginning with the value currentPage - 1
because array indexes start with 0
.
Pagination Hook: Complete Component
Here is the complete component:
1 import React, { useState } from "react";
2
3 function usePagination(data, itemsPerPage) {
4 const [currentPage, setCurrentPage] = useState(1);
5 const maxPage = Math.ceil(data.length / itemsPerPage);
6
7 function currentData() {
8 const begin = (currentPage - 1) * itemsPerPage;
9 const end = begin + itemsPerPage;
10 return data.slice(begin, end);
11 }
12
13 function next() {
14 setCurrentPage((currentPage) => Math.min(currentPage + 1, maxPage));
15 }
16
17 function prev() {
18 setCurrentPage((currentPage) => Math.max(currentPage - 1, 1));
19 }
20
21 function jump(page) {
22 const pageNumber = Math.max(1, page);
23 setCurrentPage((currentPage) => Math.min(pageNumber, maxPage));
24 }
25
26 return { next, prev, jump, currentData, currentPage, maxPage };
27 }
28
29 export default usePagination;
Conclusion
In this article, I showed how to implement a custom react hook. The hook exposes its functions and parts of its state to its caller. The caller invokes functions on the hook, and decides how to render its result and the pagination. Custom hooks are powerful and help you to define reusable functionality.
-
Yes, you can reuse built-in/custom hooks inside other hooks. ↩
Top comments (12)
Hi, Sebastian!
Got a quick question on the actual usage of the Hook (amazing work by the way).
I have used the hook as is used in the codesandbox example available in one of the comments of this post.
The problem I'm facing is that in the codesandbox example the app works with data that is available right away -it's stored in a json file within the project-, so it's accessible during the initial render. I am working with data fetched from an API that is then stored in a redux store. On the first render the data is not available.
I am getting completely logical errors about reading and accessing data in a variable that holds undefined as a value until the request to the server is done, answered and stored.
I have been trying to think of a work around using the currentData() method, or trying to serve a different component until the data is populated but I get errors about rendering more hooks than the initial render (as the hooks should be on the top level and I would achieve this behaviour with and if/else statement).
Do you have any idea on how could I use this hook with async data? Been thinking about it for a while now but couldn't figure it out.
Thanks in advance!
For anyone reading this question, I could figure out how to solve this issue with a simple feature. I added an empty array as a default parameter to the "data" parameter in the Hook.
Posting your own solution to helps other that may face the same issue deserves recognition. Thank you!
One thing I think some users would like to see is how you implemented this in your application. I use pagination code very similar and avoid Redux as well and was curious how you were using the pagination and how big is your data-set you are using with it?
Hi Jax, I'm using it for the board game search component and user timeline entries. When searching for a board game, some queries return more than 30 games. With the pagination hook, I neatly divide them into buckets of 10 game to show in a table.
a code implementation of how you use it would be helpful :D
I found this example: codesandbox.io/s/react-hooks-mater...
Exactly what I'm looking for. Thanks man, such a big help!
Thank You Sebastian this helped me alot
Hi,@admantium great post about usePagination custom-hooks .can you please help how to use it in a table which shows list of items. regards
fiaz ahmed ranjha
Hi, I'm new in React, I start with one Api in REact , redux , hooks , but how can insert your Code in mi list code .map() , I use Thunk too. Sorry my english is horrible , I'm from Perú. Thanks
nice - where do you set itemsPerPage?