Google Map React Component Tutorial

Google Map React Component Tutorial

Google Map React Component Tutorial .isomorphic google map react component, allows render react components on the google map.

Fullstack React Google Maps Tutorial

A declarative Google Map React component using React, lazy-loading dependencies, current-location finder and a test-driven approach by the Fullstack React team.

See the demo and accompanying blog post.

Quickstart

First, install the library:

source-shell
npm install --save google-maps-react
Automatically Lazy-loading Google API

The library includes a helper to wrap around the Google maps API. The GoogleApiWrapper Higher-Order component accepts a configuration object which must include an apiKey. See lib/GoogleApi.js for all options it accepts.

source-js
import {GoogleApiWrapper} from 'google-maps-react';

// ...

export class MapContainer extends React.Component {}

export default GoogleApiWrapper({
  apiKey: (YOUR_GOOGLE_API_KEY_GOES_HERE)
})(MapContainer)

Alternatively, the GoogleApiWrapper Higher-Order component can be configured by passing a function that will be called with whe wrapped component's props and should returned the configuration object.

source-js
export default GoogleApiWrapper(
  (props) => ({
    apiKey: props.apiKey,
    language: props.language,
  }
))(MapContainer)

If you want to add a loading container other than the default loading container, simply pass it in the HOC, like so:

source-js
const LoadingContainer = (props) => (
  <div>Fancy loading container!</div>
)

export default GoogleApiWrapper({
  apiKey: (YOUR_GOOGLE_API_KEY_GOES_HERE),
  LoadingContainer: LoadingContainer
})(MapContainer)
Sample Usage With Lazy-loading Google API:
source-js
import {Map, InfoWindow, Marker, GoogleApiWrapper} from 'google-maps-react';

export class MapContainer extends Component {
  render() {
    return (
      <Map google={this.props.google} zoom={14}>

        <Marker onClick={this.onMarkerClick}
                name={'Current location'} />

        <InfoWindow onClose={this.onInfoWindowClose}>
            <div>
              <h1>{this.state.selectedPlace.name}</h1>
            </div>
        </InfoWindow>
      </Map>
    );
  }
}

export default GoogleApiWrapper({
  apiKey: (YOUR_GOOGLE_API_KEY_GOES_HERE)
})(MapContainer)

Examples

Check out the example site at: http://fullstackreact.github.io/google-maps-react

Additional Map Props

The Map component takes a number of optional props.

Zoom: (Shown Above) takes a number with the higher value representing a tighter focus on the map's center.

Style: Takes CSS style object - commonly width and height.

source-js
const style = {
  width: '100%',
  height: '100%'
}

initalCenter: Takes an object containing latitude and longitude coordinates. Sets the maps center upon loading.

source-js
    <Map
          google={this.props.google}
          style={style}
          initialCenter={{
            lat: 40.854885,
            lng: -88.081807
          }}
          zoom={15}
          onClick={this.onMapClicked}
        >

center: Takes an object containing latitude and longitude coordinates. Use this if you want to re-render the map after the initial render.

source-js
    <Map
          google={this.props.google}
          style={style}
          center={{
            lat: 40.854885,
            lng: -88.081807
          }}
          zoom={15}
          onClick={this.onMapClicked}
        >

bounds: Takes a google.maps.LatLngBounds() object to adjust the center and zoom of the map.

source-js
var points = [
    { lat: 42.02, lng: -77.01 },
    { lat: 42.03, lng: -77.02 },
    { lat: 41.03, lng: -77.04 },
    { lat: 42.05, lng: -77.02 }
]
var bounds = new this.props.google.maps.LatLngBounds();
for (var i = 0; i < points.length; i++) {
  bounds.extend(points[i]);
}
return (
    <Map
        google={this.props.google}
        initialCenter={{
            lat: 42.39,
            lng: -72.52
        }}
        bounds={bounds}>
    </Map>
);

The following props are boolean values for map behavior: scrollwheel, draggable, keyboardShortcuts, disableDoubleClickZoom

The following props are boolean values for presence of controls on the map: zoomControl, mapTypeControl, scaleControl, streetViewControl, panControl, rotateControl, fullscreenControl

The following props are object values for control options such as placement of controls on the map: zoomControlOptions, mapTypeControlOptions, streetViewControlOptions See Google Maps Controls for more information.

It also takes event handlers described below:

Events

The <Map /> component handles events out of the box. All event handlers are optional.

onReady

When the <Map /> instance has been loaded and is ready on the page, it will call the onReady prop, if given. The onReady prop is useful for fetching places or using the autocomplete API for places.

source-js
fetchPlaces(mapProps, map) {
  const {google} = mapProps;
  const service = new google.maps.places.PlacesService(map);
  // ...
}

render() {
  return (
    <Map google={this.props.google}
      onReady={this.fetchPlaces}
      visible={false}>
        <Listing places={this.state.places} />
    </Map>
  )
}

onClick

To listen for clicks on the <Map /> component, pass the onClick prop:

source-js
mapClicked(mapProps, map, clickEvent) {
  // ...
}

render() {
  return (
    <Map google={this.props.google}
      onClick={this.mapClicked} />
  )
}

onDragend

When our user changes the map center by dragging the Map around, we can get a callback after the event is fired with the onDragend prop:

source-js
centerMoved(mapProps, map) {
  // ...
}

render() {
  return (
    <Map google={this.props.google}
      onDragend={this.centerMoved} />
  )
}

The <Map /> component also listens to onRecenter, onBoundsChanged, onCenterChanged, onDblclick, onDragstart, onHeadingChange, onIdle, onMaptypeidChanged, onMousemove, onMouseout, onMouseover, onProjectionChanged, onResize, onRightclick, onTilesloaded, onTiltChanged, and onZoomChanged events. See Google Maps Events for more information.

Visibility

You can control the visibility of the map by using the visible prop. This is useful for situations when you want to use the Google Maps API without a map. The <Map /> component will load like normal. See the Google places demo

For example:

source-js
<Map google={this.props.google}
    visible={false}>
  <Listing places={this.state.places} />
</Map>
Subcomponents

The <Map /> api includes subcomponents intended on being used as children of the Map component. Any child can be used within the Map component and will receive the three props (as children):

  • map - the Google instance of the map
  • google - a reference to the window.google object
  • mapCenter - the google.maps.LatLng() object referring to the center of the map instance

Marker

To place a marker on the Map, include it as a child of the <Map /> component.

source-js
<Map google={this.props.google}
    style={{width: '100%', height: '100%', position: 'relative'}}
    className={'map'}
    zoom={14}>
  <Marker
    title={'The marker`s title will appear as a tooltip.'}
    name={'SOMA'}
    position={{lat: 37.778519, lng: -122.405640}} />
  <Marker
    name={'Dolores park'}
    position={{lat: 37.759703, lng: -122.428093}} />
  <Marker />
  <Marker
    name={'Your position'}
    position={{lat: 37.762391, lng: -122.439192}}
    icon={{
      url: "/path/to/custom_icon.png",
      anchor: new google.maps.Point(32,32),
      scaledSize: new google.maps.Size(64,64)
    }} />
</Map>

The <Marker /> component accepts a position prop that defines the location for the position on the map. It can be either a raw object or a google.maps.LatLng() instance.

If no position is passed in the props, the marker will default to the current position of the map, i.e. the mapCenter prop.

You can also pass any other props you want with the <Marker />. It will be passed back through marker events.

The marker component can also accept a child InfoMarker component for situations where there is only 1 marker and 1 infowindow.

source-js
<Marker
  title="Location"
  id={1}
  position={markerCenter}
  draggable={true}
  onDragend={this.moveMarker.bind(this)}
  >
  <InfoWindow
    visible={showInfoWindow}
    style={styles.infoWindow}
    >
      <div className={classes.infoWindow}>
        <p>Click on the map or drag the marker to select location where the incident occurred</p>
      </div>
  </InfoWindow>
</Marker>

Events

The <Marker /> component listens for events, similar to the <Map /> component.

onClick

You can listen for an onClick event with the (appropriately named) onClick prop.

source-js
onMarkerClick(props, marker, e) {
  // ..
}

render() {
  return (
    <Map google={this.props.google}>
      <Marker onClick={this.onMarkerClick}
          name={'Current location'} />
    </Map>
  )
}

mouseover

You can also pass a callback when the user mouses over a <Marker /> instance by passing the onMouseover callback:

source-js
onMouseoverMarker(props, marker, e) {
  // ..
}

render() {
  return (
    <Map google={this.props.google}>
      <Marker onMouseover={this.onMouseoverMarker}
          name={'Current location'} />
    </Map>
  )
}

Polygon

To place a polygon on the Map, set <Polygon /> as child of Map component.

source-js
render() {
  const triangleCoords = [
    {lat: 25.774, lng: -80.190},
    {lat: 18.466, lng: -66.118},
    {lat: 32.321, lng: -64.757},
    {lat: 25.774, lng: -80.190}
  ];

  return(
    <Map google={this.props.google}
        style={{width: '100%', height: '100%', position: 'relative'}}
        className={'map'}
        zoom={14}>
        <Polygon
          paths={triangleCoords}
          strokeColor="#0000FF"
          strokeOpacity={0.8}
          strokeWeight={2}
          fillColor="#0000FF"
          fillOpacity={0.35} />
    </Map>
  )
}

[Events

The <Polygon /> component listens to onClick, onMouseover and onMouseout events.

Polyline

To place a polyline on the Map, set <Polyline /> as child of Map component.

source-js
render() {
  const triangleCoords = [
    {lat: 25.774, lng: -80.190},
    {lat: 18.466, lng: -66.118},
    {lat: 32.321, lng: -64.757},
    {lat: 25.774, lng: -80.190}
  ];

  return(
    <Map google={this.props.google}
        style={{width: '100%', height: '100%', position: 'relative'}}
        className={'map'}
        zoom={14}>
        <Polyline
          path={triangleCoords}
          strokeColor="#0000FF"
          strokeOpacity={0.8}
          strokeWeight={2} />
    </Map>
  )
}

Events

The <Polyline /> component listens to onClick, onMouseover and onMouseout events.

InfoWindow

The <InfoWindow /> component included in this library is gives us the ability to pop up a "more info" window on our Google map.

The visibility of the <InfoWindow /> component is controlled by a visible prop. The visible prop is a boolean (PropTypes.bool) that shows the <InfoWindow /> when true and hides it when false.

There are two ways how to control a position of the <InfoWindow /> component. You can use a position prop or connect the <InfoWindow /> component directly to an existing <Marker /> component by using a marker prop.

source-js
//note: code formatted for ES6 here
export class MapContainer extends Component {
  state = {
    showingInfoWindow: false,
    activeMarker: {},
    selectedPlace: {},
  };

  onMarkerClick = (props, marker, e) =>
    this.setState({
      selectedPlace: props,
      activeMarker: marker,
      showingInfoWindow: true
    });

  onMapClicked = (props) => {
    if (this.state.showingInfoWindow) {
      this.setState({
        showingInfoWindow: false,
        activeMarker: null
      })
    }
  };

  render() {
    return (
      <Map google={this.props.google}
          onClick={this.onMapClicked}>
        <Marker onClick={this.onMarkerClick}
                name={'Current location'} />

        <InfoWindow
          marker={this.state.activeMarker}
          visible={this.state.showingInfoWindow}>
            <div>
              <h1>{this.state.selectedPlace.name}</h1>
            </div>
        </InfoWindow>
      </Map>
    )
  }
}

Events

The <InfoWindow /> throws events when it's showing/hiding. Every event is optional and can accept a handler to be called when the event is fired.

source-js
<InfoWindow
  onOpen={this.windowHasOpened}
  onClose={this.windowHasClosed}
  visible={this.state.showingInfoWindow}>
    <div>
      <h1>{this.state.selectedPlace.name}</h1>
    </div>
</InfoWindow>

onClose

The onClose event is fired when the <InfoWindow /> has been closed. It's useful for changing state in the parent component to keep track of the state of the <InfoWindow />.

onOpen

The onOpen event is fired when the window has been mounted in the Google map instance. It's useful for keeping track of the state of the <InfoWindow /> from within the parent component.

Circle

To place a circle on the Map, set <Circle /> as child of Map component.

source-js
render() {
  const coords = { lat: -21.805149, lng: -49.0921657 };

  return (
    <Map
      initialCenter={coords}
      google={this.props.google}
      style={{width: 500, height: 500, position: 'relative'}}
      zoom={14}
    >
      <Circle
        radius={1200}
        center={coords}
        onMouseover={() => console.log('mouseover')}
        onClick={() => console.log('click')}
        onMouseout={() => console.log('mouseout')}
        strokeColor='transparent'
        strokeOpacity={0}
        strokeWeight={5}
        fillColor='#FF0000'
        fillOpacity={0.2}
      />
    </Map>
  );
}

Events

The <Circle /> component listens to onClick, onMouseover and onMouseout events.

The GoogleApiWrapper automatically passes the google instance loaded when the component mounts (and will only load it once).

Manually loading the Google API

If you prefer not to use the automatic loading option, you can also pass the window.google instance as a prop to your <Map /> component.

source-js
<Map google={window.google} />
Issues?

If you have some issues, please make an issue on the issues tab and try to include an example. We've had success with https://codesandbox.io

An example template might look like: https://codesandbox.io/s/rzwrk2854

Contributing
source-shell
git clone https://github.com/fullstackreact/google-maps-react.git
cd google-maps-react
npm install
make dev

The Google Map React component library uses React and the Google API to give easy access to the Google Maps library.

Programming a Javascript Simon Game Tutorial

Programming a Javascript Simon Game Tutorial

In this javascript tutorial, I recorded myself live programming an html5 javascript simon game.

In this javascript tutorial, I recorded myself live programming an html5 javascript simon game.

For those who don't know, I'm a full stack web developer who has been in the industry for over 5 years now. There is a lot of things I have learned along the way and I'd like to share that knowledge with anyone wanting to learn!

like this video if you found it useful and would like to see more videos of the same content.

subscribe to my channel if you are trying to improve your abilities as a web developer, software engineer, or even if you are just learning to code.

Don't forget to turn on those bell notifications!

Understanding Memoization And Dynamic Programming in Javascript

Understanding Memoization And Dynamic Programming in Javascript

In this Javascript tutorial I will explain what memoization is, how to use it, when you should use memoization, how to use memoization, what dynamic programming is, how to use memoization in dynamic programming. Memoization is a big complicated word that you may have never even heard before, but you may be surprised to know that you are most likely already using memoization without even realizing it.

Memoization is a big complicated word that you may have never even heard before, but you may be surprised to know that you are most likely already using memoization without even realizing it. Memoization is just the act of caching values so that they can be calculated quicker in the future. Memoization is really useful in all parts of programming, but where it is most useful is in dynamic programming. In this video I will explain what memoization is, how to use it, and why it is so useful especially in dynamic programming.

🧠 Concepts Covered:

  • What memoization is
  • When you should use memoization
  • How to use memoization
  • What dynamic programming is
  • How to use memoization in dynamic programming

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.