React PWA Tutorial

React PWA Tutorial

Introduction

        React is one of the most popular JavaScript libraries for building user interfaces. Progressive Web Application (PWA) has been present for quite some time, but now it is becoming more and more popular. I searched online for a simple post about React and PWA, but there are so many confusing posts and most of the times I see either how to build a React App or how to build a PWA, but they do not give a full picture of both. Hence I decided to write this post. This blog post will give you a comprehensive overview of the following:
  1. Building a simple React App
  2. Converting the React App into a PWA
  3. Deploying the PWA on Firebase

Progressive Web Application (PWA)

        Why do we need a PWA? The advantages of a PWA over a regular web application are, it is:
  1. Responsive: It can fit any screen size
  2. Reliable: It can load fast and can work seamlessly in poor network conditions
  3. Fast: It can respond fast to user interactions and page scrolling and animation loads happen without any lags. 
  4. Engaging: It can receive push notifications and on a cell phone or mobile device it can be launched from the home screen just like any other App
        So PWA is a hybrid between a web application and a mobile application. Since the usage of mobile devices are increasing day by day, creating a PWA is advantageous as it works reliably no matter how the network conditions are. It also keeps the people engaged as it is slick in operation and thus helps to retain a huge customer base. PWA can also be used to send push notifications and it acts just like any other App on the home screen. For a PWA, there is no need of download or installation required from the App store. Hence building a PWA is a win-win situation for any organization/developer.

Requirements to Run the Application:
  1. Node.js
  2. Node Package Manager (NPM)
  3. Visual Studio Code or any IDE that supports working on UI code.
Node.js, NPM and React CLI should be setup and running in your machine. To setup, run and test if all these are working fine, please refer to my post below:
Nodejs and React CLI Setup.

I prefer using Visual Studio Code for working on the UI code. It can be downloaded from Microsoft Visual Studio Code Website.
To get to know more about Visual Studio Code, you can read: Getting Started on Visual Studio Code
If you prefer using any other tool for working on the UI code, you can go ahead and use it.

In this post, I am building a simple UI which fetches data from a public Cryptocurrency API and displays it
Once you have the initial setup mentioned above, you can build the application in few simple steps.

Step 1: Create a new Project, call the API and define the styling for the page and the data table

Use React CLI to create a new React project and start the application by executing the commands:
$ create-react-app ReactPWA
$ cd ReactPWA
$ npm start
In our application, we will fetch the latest rates of the popular Cryptocurrencies in US Dollar (USD) and display them. The Cryptocurrency rates are fetched using a publicly available CryptoCompare API. To connect to this API, we will use HTTP client library Axios. To install this library execute the command:
$ npm install axios --save 
We will also use the library react-number-format to format USD amount values. To install this library execute the command:
$ npm install react-number-format --save 
Open the file src/App.js and add the following code in it:
import React, { Component } from 'react';
import './App.css';
import axios from 'axios';
var NumberFormat = require('react-number-format');

class App extends Component {

  constructor(props) {
    super(props);

    this.state = {
      cryptocurrencies: []
    };
  }

  componentDidMount() {
    axios.get('https://min-api.cryptocompare.com/data/pricemulti?fsyms=BTC,ETH,XRP,EOS,TRX&tsyms=USD')
      .then(res => {
        const cryptocurrencies = res.data;
        console.log(cryptocurrencies);
        this.setState({cryptocurrencies: cryptocurrencies});
      })
  }

  render() {
    const topbanner = {
      color: "white",
      backgroundColor: "RoyalBlue",
      padding: "15px",
      fontFamily: "Verdana"
    };
    return (
      <div className="App">
        <h1 style={topbanner}>ReactJS PWA App</h1>
        <p align="left">The popular Cryptocurrencies are:</p>
        <ol type="1">
          <li>Bitcoin (BTC)</li>
          <li>Ethereum (ETH)</li>
          <li>XRP (XRP)</li>
          <li>EOS (EOS)</li>
          <li>TRON (TRX)</li>
        </ol>

        <p align="left">Here are their current rates:</p>
        {Object.keys(this.state.cryptocurrencies).map((key) => (

          <div id="cryptocurrency-container">
            <span className="left">{key}</span>
            <span className="right"><NumberFormat value={this.state.cryptocurrencies[key].USD} displayType={'text'} decimalPrecision={2} thousandSeparator={true} prefix={'$'} /></span>
          </div>

        ))}
        <p align="center"><b>#SoftwareDeveloperCentral @AjTechDeveloper</b></p>
      </div>
    );
  }
}

export default App; 
Here is the explanation for the code above:

Initially we import all the libraries used in this file. Then we create a constructor to pass in properties (props). Then the property cryptocurrencies is defined.This property is used to hold the response data from the CryptoCompare API.
The function componentDidMount is used along with axios to call and get data from the API. Here is the code used for this:
componentDidMount() {
    axios.get('https://min-api.cryptocompare.com/data/pricemulti?fsyms=BTC,ETH,XRP,EOS,TRX&tsyms=USD')
      .then(res => {
        const cryptocurrencies = res.data;
        console.log(cryptocurrencies);
        this.setState({cryptocurrencies: cryptocurrencies});
      })
  }
The code below this is used to define the template that is used in the web page. Here the new container (cryptocurrency-container) is used to display the Cryptocurrency rates. The styling for this needs to be defined in src/App.css. Open this file and add the code below:
body {
  background: #f3eeee;
  padding:15px;
}

div#cryptocurrency-container {
  background:#00BFFF;
  width: 75%;
  margin: 0 auto 4px auto;
  padding: 1em;
  box-shadow: 1px 1px 0 lightgrey;
}

span.left {
  font-weight: bold;
}

span.right {
  float:right;
}
Start the App using the following commands:
$ npm install
$ npm start
You can now view the App using the following URL:
http://localhost:3000

Step 2: Convert the Application to a PWA

        First step here is to understand Service Worker 

Service Worker

        Service Worker is a JavaScript file that runs separately from the main browser thread. It intercepts network requests and caches or retrieves resources from the cache. It also delivers push messages.Service workers enable applications to control network requests, cache those requests to improve performance, and provide offline access to cached content. Using Service Worker PWAs can load immediately and also provide a reliable user experience in poor network conditions also.

Service Worker to be created and registered

A new JavaScript file: svcWorker.js needs to be created in the public folder. Add the following code in it:
var CACHE_NAME = 'task-manager-pwa';
var urlsToCache = [
  '/',
  '/completed'
];

// Install service worker
self.addEventListener('install', event => {
  // Perform the install steps
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then(function(cache) {
        console.log('Cache opened');
        return cache.addAll(urlsToCache);
      })
  );
});

// Cache and return the requests
self.addEventListener('fetch', event => {
  event.respondWith(
    caches.match(event.request)
      .then(function(response) {
        // Return response as Cache is hit 
        if (response) {
          return response;
        }
        return fetch(event.request);
      }
    )
  );
});

// Update service worker
self.addEventListener('activate', event => {
  var cacheWhitelist = ['task-manager-pwa'];
  event.waitUntil(
    caches.keys().then(cacheNames => {
      return Promise.all(
        cacheNames.map(cacheName => {
          if (cacheWhitelist.indexOf(cacheName) === -1) {
            return caches.delete(cacheName);
          }
        })
      );
    })
  );
});
In src/index.js, use the code: serviceWorker.register(); 
Here is full code in src/index.js:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';

ReactDOM.render(<App />, document.getElementById('root'));

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.register();
Make changes to the index.html file to check if the client browser supports service workers and also the application renders styling and works fine without any trace of JavaScript loading. Code for this is added between the <style> </style> and <body> </body> tags. Here is the code in index.html:
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1"
    />
    <meta name="theme-color" content="#000000" />
    <meta
      name="description"
      content="Web site created using create-react-app"
    />
    <link rel="apple-touch-icon" href="logo512.png" />
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
    <title>React PWA App</title>
    <style type="text/css">
      body {
        margin: 0;
        padding: 0;
        font-family: sans-serif;
      }
      .navbar {
        background-color: #4169e1;
      }
      .navbar h3 {
        display: inline-block;
        text-align: left;
        padding: 10px;
        color: black;
        text-decoration: none;
      }
      .navbar a {
        display: inline-block;
        padding: 10px;
        color: #fff;
        text-decoration: none;
      }
      .page-info {
        padding: 10px;
      }
      .Current {
        color: #2e8b57;
      }
      .Completed {
        color: #ff6347;
        text-decoration: line-through;
      }
    </style>
  </head>
  <body>
    <noscript>Enable JavaScript to run this app.</noscript>
    <div id="root">
      <div class="navbar">
        <h3>Task Manager</h3>
        <a href="/">Present Tasks</a>
        <a href="/completed">Finished Tasks</a>
      </div>
      <p class="page-info">
        App Loading...
      </p>
    </div>
    <script>
      if ('serviceWorker' in navigator) {
        window.addEventListener('load', function() {
          navigator.serviceWorker.register('svcWorker.js').then(function(registration) {
            console.log('Worker registration is successful', registration.scope);
          }, function(err) {
            console.log('Worker registration has failed', err);
          }).catch(function(err) {
            console.log(err);
          });
        });
      } else {
        console.log('Service Worker is not supported by your browser.');
      }
    </script>
  </body>
</html>
Execute the following commands:
$ npm install
Create a production build:
$ npm run build
Start the app:
$ npm start
Load the App in a browser:
http://localhost:3000

Add Lighthouse extension to Chrome and in Lighthouse, click on the button Generate report
Here is the report generated in Lighthouse. Only issue is redirection of HTTP traffic to HTTPS. This will be resolved when the app is deployed in any environment. You can use any environment like AWS, Heroku...I am using Firebase for deploying the app.

Step 3: React PWA Deployment 

        I am using Firebase to fix the PWA audit item related to redirecting to HTTPS. Here are the steps to use Firebase:
  • Login to Firebase using your Google login
  • In the Firebase console, create a new project with any name using the Add project button. I am using the name ReactPWA
  • Install the Firebase CLI globally to your node modules using the command:
    $ npm install -g firebase-tools
    
  • Login to Firebase using the command:
    firebase login
  • Navigate to the React PWA project folder and initialize Firebase in this location using the command:
    firebase init
    Then choose Hosting option using the Down Arrow button to navigate and SpaceBar to select and Enter Button to confirm a choice as below:
    ? Which Firebase CLI features do you want to setup for this folder? Press Space to select features, then Enter to confirm your choices.
     ◯ Database: Deploy Firebase Realtime Database Rules
     ◯ Firestore: Deploy rules and create indexes for Firestore
     ◯ Functions: Configure and deploy Cloud Functions
    ❯◯ Hosting: Configure and deploy Firebase Hosting sites
     ◯ Storage: Deploy Cloud Storage security rules
    
  • Then answer the configuration steps as below:
    ? What do you want to use as your public directory? build
    ? Configure as a single-page app (rewrite all urls to /index.html)? Yes
    ? File public/index.html already exists. Overwrite? No
    
  • Firebase initialization is now complete. To deploy React PWA App in Firebase, use the command:
    firebase deploy
    Project Console URL and Hosting URL are shown. Use the Hosting URL to view the React PWA App in your browser
This app can now be viewed online using the URL: https://reactpwa-b5616.firebaseapp.com
Here is the Lighthouse report

Conclusion and GitHub link:   

    In this post I have shown you as to how you can create a simple React PWA and deploy it on Firebase. The code used in this post is available on GitHub.
    Learn the most popular and trending technologies like Machine Learning, Chatbots, Internet of Things (IoT), Big Data Processing, Elastic Stack, Angular 5, Akka HTTP, Play Framework, Dropwizard, Docker, Netflix Eureka, Netflix Zuul, Spring Cloud, Spring Boot, Flask and RESTful Web Service integration with MongoDB, Kafka, Redis, Aerospike, MySQL DB in simple steps by reading my most popular blog posts at Software Developer Central.
    If you like my post, please feel free to share it using the share button just below this paragraph or next to the heading of the post. You can also tweet with #SoftwareDeveloperCentral on Twitter. To get a notification on my latest posts or to keep the conversation going, you can follow me on Twitter or Instagram. Please leave a note below if you have any questions or comments.

Comments

Post a Comment

Popular Posts

Golang gRPC Microservice

Dropwizard MySQL Integration Tutorial

Asynchronous Processing (@Async) in Spring Boot