React Hooks Tutorial for Beginners: Getting Started With React Hooks

React Hooks Tutorial for Beginners: Getting Started With React Hooks

React hooks tutorial for beginners: learn React hooks step by step, with a look at how the same logic would be implemented with ES6 classes. In this React hooks tutorial you will learn how to use React hooks, what they are, and why we’re doing it!

Learn React hooks step by step, with a look at how the same logic would be implemented with ES6 classes. In this React hooks tutorial you will learn how to use React hooks, what they are, and why we’re doing it!

Here I am, writing a React hooks tutorial for you. I decided to wait until hooks got finally released before dropping this post. Together we’ll learn React hooks step by step, with a look at how the same logic would be implemented with ES6 classes.

Enjoy the reading!

React Hooks Tutorial for Beginners: what you will learn

In the following tutorial you’ll learn:

  • how to use React hooks
  • how the same logic would be implemented in React class components
React Hooks Tutorial for Beginners: requirements

To follow along with the tutorial you should have a basic understanding of:

  • how to use React hooks
  • how the same logic would be implemented in React class components
React Hooks Tutorial for Beginners: setting up the project

If you want to follow along with the examples make sure to configure a React development environment. Run:

npx create-react-app exploring-hooks


and you’re good to go!

(You should have one of the latest version of Node.js for running npx).

React Hooks Tutorial for Beginners: in the beginning there was setState

I won’t go too deep here, I assume you’re already using React in your project but let me do a quick recap.

React is a library for building user interfaces and one of its perks is that the library itself imposes a strict data flow to the developer. Do you remember jQuery? With jQuery it’s almost impossible to clearly structure a project, let alone defining how the data should flow across the UI. It’s hard to keep track of what function is changing what piece of UI.

The same applies to plain JavaScript: even if with self-disclipine and practice it’s possibile to come up with a well structured project (thinking about the module pattern), good luck tracking state and interactions between functions (it can be done but it’s hard without external help, see Redux).

These problems has been somewhat eased by React: by enforcing a clear structure (container and functional components) and a strict data flow (components react to state and props change) now its easier than before to create well reasoned UI logic.

So the theory in React is that a piece of UI can “react” in response to a state change. The basic form for expressing this flow was an ES6 class up until now. Consider the following example, an ES6 class extending from React.Component, with an internal state:

import React, { Component } from "react";

export default class Button extends Component {
  constructor() {
    super();
    this.state = { buttonText: "Click me, please" };
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    this.setState(() => {
      return { buttonText: "Thanks, been clicked!" };
    });
  }

  render() {
    const { buttonText } = this.state;
    return <button onClick={this.handleClick}>{buttonText}</button>;
  }
}


As you can see from the code above the component’s internal state gets mutated by setState when clicking the button. The text’s button in turns reacts to this change and gets the updated text.

A more concise version of the component can be expressed by removing the constructor and leveraging the ECMAScript class field proposal:

import React, { Component } from "react";

export default class Button extends Component {
  state = { buttonText: "Click me, please" };

  handleClick = () => {
    this.setState(() => {
      return { buttonText: "Thanks, been clicked!" };
    });
  };

  render() {
    const { buttonText } = this.state;
    return <button onClick={this.handleClick}>{buttonText}</button>;
  }
}


So, in the beginning there was setState (and still it will be). But keep calm. The style above is perfectly fine and ES6 classes in React won’t go away anytime soon.

But now with React hooks it’s possible to express the flow internal state change -> UI reaction without using an ES6 class.

Follow me into the next section …

React Hooks Tutorial for Beginners: updating the state in React … without setState

So what options do we have for managing the internal state in React now that setState and classes are not a need anymore?

Enter the first and most important React hook: useState. useState is a function exposed by the react package. You will import that function at the top of your files as:

import React, { useState } from "react";


By importing useState in your code you’re signaling the intent to hold some kind of state inside your React component. And more important, that React component shouldn’t be an ES6 class anymore. It can be a pure and simple JavaScript function. This is the most appealing thing of this hooks story.

After importing useState you’ll pick an array containing two variables out of useState, and the code should go inside your React component:

const [buttonText, setButtonText] = useState("Click me, please")


Confused by this syntax? It’s ES6 destructuring. The names above can be anything you want, it doesn’t matter for React. Anyway I advise using descriptive and meaningful variable names depending on the state’s purpose.

The argument passed to useState is the actual starting state, the data that will be subject to changes. useState returns for you two bindings:

  • how to use React hooks
  • how the same logic would be implemented in React class components

So the previous example, a button component, with hooks becomes:

import React, { useState } from "react";

export default function Button() {
  const [buttonText, setButtonText] = useState("Click me, please");

  return (
    <button onClick={() => setButtonText("Thanks, been clicked!")}>
      {buttonText}
    </button>
  );
}


For calling the setButtonText state updater inside the onClick handler you can use an inline arrow function. But if you prefer using a regular function you can do:

import React, { useState } from "react";

export default function Button() {
  const [buttonText, setButtonText] = useState("Click me, please");

  function handleClick() {
    return setButtonText("Thanks, been clicked!");
  }

  return <button onClick={handleClick}>{buttonText}</button>;
}


Must be honest, I fancy regular functions more than arrow functions, unless I have specific requirements. Readability improves a lot. Also, when I write code I think always of the next developer that will mantain that code. And my code should be readable.

React hooks, that’s it! I could end this post here but not before showing you how to fetch data with hooks.

Head over the next section!

React Hooks Tutorial for Beginners: in the beginning there was componentDidMount (and render props)

Data fetching in React! Do you remember the old days of componentDidMount? You would slap fetch(url) in componentDidMount and call it a day. Here’s how to fetch an array of data from an API for rendering out a nice list:

import React, { Component } from "react";

export default class DataLoader extends Component {
  state = { data: [] };

  componentDidMount() {
    fetch("http://localhost:3001/links/")
      .then(response => response.json())
      .then(data =>
        this.setState(() => {
          return { data };
        })
      );
  }

  render() {
    return (
      <div>
        <ul>
          {this.state.data.map(el => (
            <li key={el.id}>{el.title}</li>
          ))}
        </ul>
      </div>
    );
  }
}


You could even use async/await in componentDidMount, with some caveats. But most of the asynchronous logic in my projects would live outside React components. There are yet a couple of shortcomings in the above code. The rendered list is fixed but with a render prop we can easily pass children as a function. The refactored component would look like the following:

import React, { Component } from "react";

export default class DataLoader extends Component {
  state = { data: [] };

  componentDidMount() {
    fetch("http://localhost:3001/links/")
      .then(response => response.json())
      .then(data =>
        this.setState(() => {
          return { data };
        })
      );
  }

  render() {
    return this.props.render(this.state.data);
  }
}


And you would consume the component by providing a render prop from the outside:

      <DataLoader
        render={data => {
          return (
            <div>
              <ul>
                {data.map(el => (
                  <li key={el.id}>{el.title}</li>
                ))}
              </ul>
            </div>
          );
        }}
      />


Even this pattern (born for providing a nicer alternative to mixins and HOCs) has its shortcomings. And that’s I guess the exact reason which lead React engineers to come up with hooks: provide a better ergonomics for encapsulating and reusing logic in React.

So impatient as I am, one of the first thing I wanted to try with hooks was data fetching. But what hook I’m supposed to use for fetching data? Would the component still use the render prop pattern?

Let’s see into the next section!

React Hooks Tutorial for Beginners: fetching data with useEffect

I thought data fetching with React hooks shouldn’t look so different from useState. A quick glance at the documentation gave me an hint: useEffect could be the right tool for the job.

I read: “useEffect serves the same purpose as componentDidMount, componentDidUpdate, and componentWillUnmount in React classes, but unified into a single API”

Bingo! It’s amazing, isn’t it? With this knowledge in hand I refactored the first version of Dataloader for using useEffect. The component becomes a function and fetch gets called inside useEffect. Moreover, instead of calling this.setState I can use setData (an arbitrary function extracted from useState):

import React, { useState, useEffect } from "react";

export default function DataLoader() {
  const [data, setData] = useState([]);

  useEffect(() => {
    fetch("http://localhost:3001/links/")
      .then(response => response.json())
      .then(data => setData(data));
  });

  return (
    <div>
      <ul>
        {data.map(el => (
          <li key={el.id}>{el.title}</li>
        ))}
      </ul>
    </div>
  );
}


At this point I thought “what could be wrong?” and I launched the app. This is what I saw in the console:

It was clearly my fault because I’ve already got an hint of what was going on:

"useEffect serves the same purpose as componentDidMount, componentDidUpdate, and componentWillUnmount"

componentDidUpdate! componentDidUpdate is a lifecycle method running every time a component gets new props, or a state change happens. That’s the trick. If you call useEffect like I did you would see an infinite loop. And for solving this “bug” you would need to pass an empty array as a second argument to useEffect:

//

  useEffect(() => {
    fetch("http://localhost:3001/links/")
      .then(response => response.json())
      .then(data => setData(data));
  }, []); // << super important array

//


I wish this info got the visibility it deserves rather than being at the end of this page: Using the Effect Hook. But even with this informations I wouldn’t suggest rewriting all your React components to use hooks for fetching. A lot could still change in the near future, as Ryan Florence suggests:

Anyway, useEffect replaces componentDidMount, componentDidUpdate, and componentWillUnmount, which I think is a nice thing for both experts developers and newcomers to React.

React Hooks Tutorial for Beginners: can I use render props with React hooks?

Of course! But there’s no point in doing that. Our DataLoader component becomes:

import React, { useState, useEffect } from "react";

export default function DataLoader(props) {
  const [data, setData] = useState([]);

  useEffect(() => {
    fetch("http://localhost:3001/links/")
      .then(response => response.json())
      .then(data => setData(data));
  }, []); // << super important array

  return props.render(data)
}


And you would consume the component by providing a render prop from the outside as we did in the previous example.

But again, there’s no point in this refactoring because React hooks were born for a reason: sharing logic between components, and we’ll see an example in the next section.

React Hooks Tutorial for Beginners: your first custom React hook

Instead of HOCs and render props, we can encapsulate our logic in a React hook and then import that hook whenever we feel the need. In our example we can create a custom hooks for fetching data.

A custom hook is a JavaScript function whose name starts with “use”, according to the React documentation. Easier done than said. Let’s make a useFetch hook then:

// useFetch.js
import { useState, useEffect } from "react";

export default function useFetch(url) {
  const [data, setData] = useState([]);

  useEffect(() => {
    fetch(url)
      .then(response => response.json())
      .then(data => setData(data));
  }, []);

  return data;
}


And this is how you would use the custom hook:

import React from "react";
import useFetch from "./useFetch";

export default function DataLoader(props) {
  const data = useFetch("http://localhost:3001/links/");
  return (
    <div>
      <ul>
        {data.map(el => (
          <li key={el.id}>{el.title}</li>
        ))}
      </ul>
    </div>
  );
}


This is what make hooks so appealing: finally we have a nice, standardized, and clean way for encapsulating and sharing logic.

NOTE: I didn’t account for fetch errors in the code above, do your homeworks!

React Hooks Tutorial for Beginners: can I use async/await with useEffect?

When playing with useEffect I wanted to try async/await inside the hook. Let’s see our custom hook for a moment:

// useFetch.js
import { useState, useEffect } from "react";

export default function useFetch(url) {
  const [data, setData] = useState([]);

  useEffect(() => {
    fetch(url)
      .then(response => response.json())
      .then(data => setData(data));
  }, []);

  return data;
}


For refactoring to async/await the most natural thing you would do is probably:

// useFetch.js
import { useState, useEffect } from "react";

export default function useFetch(url) {
  const [data, setData] = useState([]);

  useEffect(async () => {
    const response = await fetch(url);
    const data = await response.json();
    setData(data);
  }, []);

  return data;
}


Makes sense right? Then I opened the console and React was screaming at me:

“Warning: An Effect function must not return anything besides a function, which is used for clean-up.” Followed by a complete explanation of what I was doing wrong. How nice!

Turns out you cannot return a Promise from useEffect. JavaScript async functions always return a promise and useEffect should exclusively return another function, which is used for cleaning up the effect. That is, if you were to start setInterval in useEffect you would return a function (we have a closure there) for clearing up the interval.

So for making React happy we could rewrite our asynchronous logic like so:

// useFetch.js
import { useState, useEffect } from "react";

export default function useFetch(url) {
  const [data, setData] = useState([]);

  async function getData() {
    const response = await fetch(url);
    const data = await response.json();
    setData(data);
  }

  useEffect(() => {
    getData();
  }, []);

  return data;
}


and your custom hook will work again.

React Hooks Tutorial for Beginners: wrapping up

React hooks are a nice addition to the library. Born as an RFC in November 2018 they caught up quickly and landed in React 16.8. Think of React hooks as encapsulated states living outside your React components.

React hooks make render props and HOCs almost obsolete and provide a nicer ergonomics for sharing logic. With React hooks you can reuse common pieces of logic between React components.

React ships with a bunch of pre-defined hooks. The most important are useState and useEffect. useState makes possible to use local state inside React components, without resorting to ES6 classes.

useEffect replaces componentDidMount, componentDidUpdate, and componentWillUnmount providing a unified API. There are a lot of other hooks and I suggest reading through the official documentation for learning more.

It’s easy to foresee where React is going: functional components all over the place! But even then we will have 3 ways for expressing components in React:

  • how to use React hooks
  • how the same logic would be implemented in React class components

I can see a lot of convenience in hooks and I’m happy with the API they provide. It’s amazing how React is evolving, the community seems to find always a clever way for solving harder problems.

React Hooks Tutorial for Beginners: resources for learning React hooks

There are a lot of resources out there for learning about React hooks and admittedly some are better than this post. So here are my suggestions.

The React documentation is your first stop for learning hooks: Introducing Hooks is an high level overview of how and why hooks are here. Hooks at a glance goes a bit deeper and it’s the starting point for understanding hooks in depth.

Tania Rascia has a nice introduction on hooks with Build a CRUD App in React with Hooks. Speaking of more advanced use cases Matt Hamlin as a nice write up on useReducer, another React hooks for managing state changes.

Funnily enough useReducer resembles a lot Redux reducers. That’s a proof of how influential Redux is in the React community (that shouldn’t be a surprise since Dan Abramov is behind both Redux and React). I highly suggest learning Redux if you haven’t done yet, it will help a lot before studying useReducer.

React Hooks Tutorial for Beginners: appendix

At the beginning of the article I said: “With jQuery it’s almost impossible to clearly structure a project, let alone defining how the data should flow across the UI”.

And:

“The same applies to plain JavaScript: even if with self-disclipine and practice it’s possibile to come up with a well structured project, good luck tracking application’s state.”

But you might not need React for building user interfaces. Sometimes I build projects with vanilla JavaScript and here I am, doing fine. I use to create a simple prototype without any JavaScript library when I’m not sure what shape the project will take. In these kind of projects I rely on the module pattern for organising the code.

Being able to properly organise and document your code, even with vanilla JavaScript is a valuable asset for every JavaScript developer. For learning more about the module pattern in JavaScript I suggest reading Mastering the module pattern by Todd Motto and JavaScript design patterns by Addy Osmani.

Tracking state changes in the UI is really hard on the other hand. And for this kind of job a lot of libraries had born and die. One of my favorite is Redux and it can be used even with vanilla JavaScript.

Thanks for reading! See you next time!

Why ReactJS is better for Web Application Development?

Why ReactJS is better for Web Application Development?

Web Application Development is the point of contact for a business in today's digital era. It is important to choose the right platform for Web Application Development to build a high end Web

Web Application Development is essential for a business in today’s digital era. Finding the right platform for Web Application Development is important for building an effective Web Application that can enhance the overall customer engagement. Here’s what makes ReactJS a better option for building your next Web Application.

ReactJS Development Services | ReactJS Development Company - Chapter 247 Infotech

ReactJS Development Services | ReactJS Development Company - Chapter 247 Infotech

Chapter 247 Infotech is a leading ReactJS development company in India, USA, offering ReactJS development services at par to a spectrum of business domains from E-commerce, healthcare to Edutech at

The ease of user interface in an application drives the customer engagement. By leveraging ReactJS Development you can build Web Applications that are user centric and deliver high end user experience to tap into better business opportunities...

Pagination in ReactJs

Pagination in ReactJs

There are a lot of resourceful materials online that give good insights into pagination in ReactJs, as well as NPM packages you can easily use

There are a lot of resourceful materials online that give good insights into pagination in ReactJs, as well as NPM packages you can easily use. As much as I appreciate those materials and love to use those packages, they mostly deal with loading the whole dataset on the page first then completely handle the pagination in the frontend. I am approaching this article with the concept of loading the exact data needed on the page, then manually loading other dataset based on the request when the user clicks the pagination number display. Below is the content structure to guide us through this article:

Table of Contents
  • Project Setup
  • HTML and CSS Styling
  • Pagination Data Format
  • Sample API request
  • Displaying the initial data
  • Showing Page Number and getting Other data
Project Setup

We are going to use create-react-app v0.1.0 which has the CSS Module configured already. Open your terminal and cd to the folder you want the project installed. Then run the below command:

npx create-react-app pagination  --use-npm

The above command will download the project into the folder calledpagination. You need to cd into the folder and run npm start. If everything goes well, you will have a page that looks like below:

HTML and CSS Styling

Open the project in your favorite code editor and locate the App.js file, We need to prepare our App.js to the look exactly like the way we want it by adding the HTML code and CSS style below:

Create a new file called App.module.css in the same directory where you have your App.js, then import it into your App.js using:

import styles from './App.module.css';

I want us to handle the display of the pagination number first, below is the style and HTML structure of what we are going to use.

  render() {
    

    return (
      <div className={styles.app}>
        
        <table className={styles.table}>
          <thead>
            <tr>
              <th>S/N</th>
              <th>First Name</th>
              <th>Last Name</th>
            </tr>
          </thead>
          <tbody>
              <tr>
                <td>1</td>
                <td>Abel</td>
                <td>Agoi</td>
              </tr>
              <tr>
                <td>2</td>
                <td>Muyiwa</td>
                <td>Aregbesola</td>
              </tr>
              <tr>
                <td>3</td>
                <td>Opeyemi</td>
                <td>Agoi</td>
              </tr>
              <tr>
                <td>4</td>
                <td>Ope</td>
                <td>Aina</td>
              </tr>
          </tbody>
        </table>


        <div className={styles.pagination}>
          <span>&laquo;</span>
          <span className={styles.active}>1</span>
          <span>2</span>
          <span>3</span>
          <span>4</span>
        </div>

      </div>
    );
  }

pagination_01.js

Add the content below into your App.module.css.

.app {
    width: 50%;
    margin: 0 auto;
}

table {
  border-collapse: collapse;
  border-spacing: 0; 
}


table {
  border-collapse: separate;
  border-spacing: 0;
  color: #4a4a4d;
  font: 14px/1.4 "Helvetica Neue", Helvetica, Arial, sans-serif;
  width: 100%;
}
tr {
  overflow-x: scroll;
}
th,
td {
  padding: 15px 15px;
  vertical-align: middle;
  /* text-align: left; */
}
thead {
  font-size: 14px;
  line-height: 24px;
  font-family: Lato;
  border: 1px solid transparent;

  max-width: 100%;
  font-weight: 900;
  line-height: 24px;
  mix-blend-mode: normal;

  color: rgba(51, 51, 51, .5);
  background: rgba(255, 255, 255, .9);
}
thead tr th {
  padding: 15px 15px;
  border: 1px solid transparent;


  text-align: left;
}
tbody {
  max-width: 100%;
}
tbody tr:nth-child(odd) {
  background: #f0f0f2;
}
tbody tr:hover {
  background: #f0f0f2;
}
td {
  padding: 15px 15px;
}
td:first-child {
}


.pagination {
    margin-top: 25px;
}
.pagination span {
  cursor: pointer;
  color: black;
  float: left;
  padding: 8px 16px;
  text-decoration: none;
  transition: background-color .3s;
  border: 1px solid #ddd;
}

.pagination span.active {
  background-color: #0099FF;
  color: white;
  border: 1px solid #0099FF;
}

pagination_app.module.css

Sorry for the plenty code written so far :), I want us to have a good looking table with pagination style in place before we move into the actual paging. If everything goes well, your view should look like below:

Pagination Data Format

In most cases, when you are making API calls to an endpoint that returns a paginated data, you need to pass at least the page number with the URL, hence a sample URL will look like below:

https://reqres.in/api/users?page=2

The most important thing to take note of in the URL above is the page=2 where 2 is the page number dataset we want to get. It can be 3,4 or any number as much as the dataset we have in the backend.

The response will always contain three important data which are per_page, total and the actual data we want to loop through. A sample response looks like below:

Sample API request

Talking about making an API request to the backend, We need a backend to make the request to, I decide to use https://reqres.in/ as the API endpoint for this tutorial because it is free, always available and reliable. You can decide to make your API request directly inside your component’s ComponentDidMount() or dispatch an action to redux from your ComponentDidMount() but for the purpose of this tutorial, we are going to make the API call from the App.js componentDidMount().

Firstly, we need to set the component’s state like below inside your App.js

  state = {
    users: null,
    total: null,
    per_page: null,
    current_page: null
  }

pagination_component_state.js

users is going to be the data we are going to loop over, while total and per_page is going to help us with calculating paging logic while the current_page will be used to style the active pagination link.

The next thing we should do is create a helper method that will serve the purpose of making an HTTP request to the API endpoint and also update the state with the response data. The method will look like below:

  makeHttpRequestWithPage = async pageNumber => {
    let response = await fetch(`https://reqres.in/api/users?page=${pageNumber}`, {
      method: 'GET',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json',
      },
    });

    const data = await response.json();

    this.setState({
      users: data.data,
      total: data.total,
      per_page: data.per_page,
      current_page: data.page,
    });
  }

pagination_http_request.js

This method will accept a parameter called pageNumber so it can be reusable and will always update the state with the right data when the response is successful.

Since on page load, we need to make the HTTP request to the backend, and we are going to do this inside thecomponentDidMount() by calling the method above and passing it the first-page number we want which should be 1. Hence, the componentDidMount() will look like below:

 componentDidMount() {
    this.makeHttpRequestWithPage(1);
  }

pagination_componentDidMount.js

If we add console.dir(this.state.users) inside the render() method, below will be printed in the console

The null was before the data arrived, once the data arrived, it updates the state, hence the array of users data.

Displaying the initial data

Haven gotten the data needed, we need to loop through the data and display it. Hence we can update our render method to have below:

    let users;

    if (this.state.users !== null) {
      users = this.state.users.map(user => (
        <tr key={user.id}>
          <td>{user.id}</td>
          <td>{user.first_name}</td>
          <td>{user.last_name}</td>
        </tr>
      )); 
    }
    
    return (
      <div className={styles.app}>
        
        <table className={styles.table}>
          <thead>
            <tr>
              <th>S/N</th>
              <th>First Name</th>
              <th>Last Name</th>
            </tr>
          </thead>
          <tbody>
              { users }
          </tbody>
        </table>


        <div className={styles.pagination}>
          <span>&laquo;</span>
          <span className={styles.active}>1</span>
          <span>2</span>
          <span>3</span>
          <span>4</span>
          <span>&raquo;</span>
        </div>

      </div>
    );

gistfile1.txt

I replaced the dummy data we had inside the with the result of the loop which I equated to users. We have the assurance that when the state changes, ReactJs will automatically update the content of the table. The final stage is displaying the page logic and getting the other contents based on the page number clicked which will be sent to the API endpoint.

Showing Page Number and getting other data

Before we talk about showing page number automatically using the desired logic, I want us to manually show those numbers and make the actual API calls when the numbers are clicked. For now, we are going to hard code the pagination numbers ourselves like below:

<div className={styles.pagination}>
  <span onClick={() => this.makeHttpRequestWithPage(1)}>1</span>
  <span onClick={() => this.makeHttpRequestWithPage(2)}>2</span>
  <span onClick={() => this.makeHttpRequestWithPage(3)}>3</span>
  <span onClick={() => this.makeHttpRequestWithPage(4)}>4</span>
</div>

pagination_hard_code.js

The above code will look like below when previewed in the browser.

Notice that each span has an event handler attached to it, and I passed the page number to that event handler, so anytime we click on the pagination link, it will make a new HTTP request and update the component states, hence the user’s table data. We do not want to hard-code the links as we did above, so we need to automatically display those links.

So we’re planning on showing the page numbers for a series of pieces of data so that users can easily navigate multiple items. There are a few things that we need to know first:

  • The page that we’re on
  • Total number of items
  • Number of items per page

Good news is that we have captured all these things in our component’s state.

Next, we need to look at how we want to display the page numbers, there is a wide range of methods that people use:

  • Simple Next/Previous buttons with no numbers
  • A list of all possible pages
  • Page 1 & the last page, with the current page (and 2 above/below) shown

I personally prefer to show the very first page, that last page, and then the current page with 2 pages above & below. So for example on page 12 out of 24 pages we’d see:

1, 10, 11, 12, 13, 14, 24

This allows users to quickly navigate to the start, and to the end, as well as jump through multiple pages at once. For the purpose of this tutorial, I am going to show us how to show a list of all possible pages(item two above) then item three too.

The Arithmetic

We need to work out the total number of pages, for this, we want to take the total number of items that there are, and divide it by the number of items per page. But we want to make sure that we take that number and round it up.

So if there were 12 items in total, and we were showing 5 per page, we’d have a total of 3 pages of items. If we were to show 3 per page, we’d show 4 pages.

const pageNumbers = [];
for (let i = 1; i <= Math.ceil(this.state.meta.total / this.state.meta.per_page); i++) {
    pageNumbers.push(i);
}

page_logic_pagination.js

Haven gotten the page numbers, we need to loop through to display the span since we want to show all possible numbers first, our loop will look like below:

renderPageNumbers = pageNumbers.map(number => {
  let classes = this.state.current_page === number ? styles.active : '';

  return (
    <span key={number} className={classes} onClick={() => this.makeHttpRequestWithPage(number)}>{number}</span>
  );
});

pagination_all_numbers_loop.js

We need to update our pagination view to look like below:

<div className={styles.pagination}>
  <span onClick={() => this.makeHttpRequestWithPage(1)}>&laquo;</span>
  {renderPageNumbers}
</div>

pagination_view._01js

Congrats, we have successfully handle pagination, make HTTP request to the backend and changing the table content when user click on the page number to see.

To be sure we are on the same page, my App.js code looks like below:

import React, { Component } from 'react';
import styles from './App.module.css';

class App extends Component {


  state = {
    users: null,
    total: null,
    per_page: null,
    current_page: 1
  }


  componentDidMount() {
    this.makeHttpRequestWithPage(1);
  }


  makeHttpRequestWithPage = async pageNumber => {
    const response = await fetch(`https://reqres.in/api/users?page=${pageNumber}`, {
      method: 'GET',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json',
      },
    });

    const data = await response.json();

    this.setState({
      users: data.data,
      total: data.total,
      per_page: data.per_page,
      current_page: data.page
    });
  }


  render() {

    let users, renderPageNumbers;

    if (this.state.users !== null) {
      users = this.state.users.map(user => (
        <tr key={user.id}>
          <td>{user.id}</td>
          <td>{user.first_name}</td>
          <td>{user.last_name}</td>
        </tr>
      ));
    }

    const pageNumbers = [];
    if (this.state.total !== null) {
      for (let i = 1; i <= Math.ceil(this.state.total / this.state.per_page); i++) {
        pageNumbers.push(i);
      }


      renderPageNumbers = pageNumbers.map(number => {
        let classes = this.state.current_page === number ? styles.active : '';

        return (
          <span key={number} className={classes} onClick={() => this.makeHttpRequestWithPage(number)}>{number}</span>
        );
      });
    }

    return (


      <div className={styles.app}>

        <table className={styles.table}>
          <thead>
            <tr>
              <th>S/N</th>
              <th>First Name</th>
              <th>Last Name</th>
            </tr>
          </thead>
          <tbody>
            {users}
          </tbody>
        </table>


        <div className={styles.pagination}>
          <span onClick={() => this.makeHttpRequestWithPage(1)}>&laquo;</span>
          {renderPageNumbers}
          <span onClick={() => this.makeHttpRequestWithPage(1)}>&raquo;</span>
        </div>

      </div>
    );
  }

}

export default App;

pagination_app.js

and my view like below:

We can change the page number display logic to below since it will accommodate for large dataset.

renderPageNumbers = pageNumbers.map(number => {
  let classes = this.state.current_page === number ? styles.active : '';

  if (number == 1 || number == this.state.total || (number >= this.state.current_page - 2 && number <= this.state.current_page + 2)) {
    return (
      <span key={number} className={classes} onClick={() => this.makeHttpRequestWithPage(number)}>{number}</span>
    );
  }
});

pagination_another_display_logic.js

Thanks for reading.