Stock PWA with React

S

The main goal with this tutorial is learn how to use React to create PWA’s. You will learn to modify index.html and manifest.json files to support PWA. App will load Stock data from Alpha Vantage and loaded data will be shown with Material-UI and Recharts libraries.

Purpose and learning process

In this exercise you will learn the basics of the PWA development with React:

  • create a React app with PWA support
  • modify index.html
  • modify manifest.json
  • basic use of chrome development tools with PWA
  • fetching stocks value from the web
  • use 3th party library to display stocks data in charts

Example screenshots and video

Demo app is a Stock app, which fetches stocks value from the web and display’s data in chart.

Look example video from here: https://youtu.be/z3r9tXPEEq8.

Create a new React project

Create a new React app with create-react-app @ GitHub. You’ll need to have Node 8.16.0 or Node 10.16.0 or later version on your local development machine (but it’s not required on the server).

Use npx, npm or Yarn to create your project.

npx create-react-app stocks-app --template cra-template-pwa

You should see a following information text, if app creating went successfully. Use start command to start development server and build command to build your app into static files, which you need to copy to your server and open browser in that location.

Success! Created stocks-app at /Users/pasi/JamkReactProjects/stocks-app
Inside that directory, you can run several commands:

  yarn start
    Starts the development server.

  yarn build
    Bundles the app into static files for production.

  yarn test
    Starts the test runner.

  yarn eject
    Removes this tool and copies build dependencies, configuration files
    and scripts into the app directory. If you do this, you can’t go back!

We suggest that you begin by typing:

  cd stocks-app
  yarn start

Happy hacking!

Read more about creating React apps: Create React App.

Move web app to PWA

index.js

The production build has all the tools necessary to generate a first-class Progressive Web App, but the offline/cache-first behavior is opt-in only. By default, the build process will generate a service worker file, but it will not be registered, so it will not take control of your production web app.

In order to opt-in to the offline-first behavior, you should look for the following in their src/index.js file and use serviceWorker.register() to register Service Worker:

// 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();

As the comment states, switching serviceWorker.unregister() to serviceWorker.register() will opt you in to using the service worker and you now have a PWA.

Read more: Making a Progressive Web App.

index.html

Modify public/index.html to give a correct app name to your PWA. Replace correct <title>:

<title>Stock App</title>

manifest.json

Modify public/manifest.json file to have correct name in short_name and name attributes:

"short_name": "Stock App",
"name": "Stock App",

You can use a default icons or create a new ones. if you want to replace the icons your app uses, remember place a new icons to public folder.

package.json

If you are planning to publish your app in some sub folder in your web server, then you should open package.json and add a following homepage attribute:

{
  "name": "stocks-app",
  "homepage": "./",
  ...

Now static files are not looked from the web server’s root. Of course you need to do this, if you are planning to publish your project to some https server and you don’t have access to web root.

Build

You can run your project in local machine using a yarn start command. This should launch a browser, which opens your app to localhost.

This is good for local programming and testing, but you need to build a stati files for production and move those files to https server for testing PWA. Give a command:

yarn build

Publish

Copy your project buildfolder contents to web server, which supports HTTPS-connection. Go to your server with a chrome and test how it works from remote server.

Yes – it should work the same way.

Testing PWA

Open development tools and select “Audits”. Leave default options as they are and “Run audits”.

Now your page is tested and you should see there results – more importantly PWA results.

If everything went correctly, you should see that your app is working as a PWA. Scroll results to Progressive Web App and see there detailed results.

Application UI

You can use React UI components to build application UI or use some 3th party libraries – like Material-UI, which will be used in this exercise material.

Example UI with header text and select component:

Install Material-UI to your project:

npm install @material-ui/core --save

Use Select component to your UI. Learn more here: Select and add following code to your App.js:

import React, { useState } from 'react';
import './App.css';

// import for Material-UI
import { makeStyles } from '@material-ui/core/styles';
import InputLabel from '@material-ui/core/InputLabel';
import MenuItem from '@material-ui/core/MenuItem';
import FormControl from '@material-ui/core/FormControl';
import Select from '@material-ui/core/Select';

// styles for Material-UI
const useStyles = makeStyles(theme => ({
  formControl: {
    margin: theme.spacing(1),
    minWidth: 120,
  },
  selectEmpty: {
    marginTop: theme.spacing(2),
  },
}));

function App() {
  // material-UI classes
  const classes = useStyles();
  // hook for stock symbol
  const [symbol, setSymbol] = useState('');

  // load stocks
  const loadStock = (symbol) => {
    console.log(symbol);
  }

  // handle stock symbol change from InputLabel
  const handleChange = event => {
    setSymbol(event.target.value);
    loadStock(event.target.value);
  }

  return (
    <div className="App">
      <h1>Stock - Time Series</h1>
      <FormControl className={classes.formControl}>
        <InputLabel id="demo-simple-select-label">Symbol</InputLabel>
        <Select
          labelId="demo-simple-select-label"
          id="demo-simple-select"
          value={symbol}
          onChange={handleChange}
        >
          <MenuItem value='AAPL'>Apple</MenuItem>
          <MenuItem value='AMZN'>Amazon</MenuItem>
          <MenuItem value='NOK'>Nokia</MenuItem>
          <MenuItem value='TSLA'>Tesla</MenuItem>
        </Select>
      </FormControl>
    </div>
  );
}

export default App;

Test

Test your app. It should now print a selected stock symbol in your browser’s inspector.

Stocks data – Alpha Vantage

You can use any stocks data provider, but now instructions are with Alpha Vantage. Alpha Vantage is composed of a tight-knit community of researchers, engineers, and business professionals. Alpha Vantage Inc. is a leading provider of free APIs for realtime and historical data on stocks, forex (FX), and digital/crypto currencies.

Get a free API key and learn how to get stocks data.

Data url for example with Nokia:

https://www.alphavantage.co/query?function=TIME_SERIES_DAILY&symbol=NOK&apikey=YOUR_API_KEY

Returned JSON structure:

{
    "Meta Data": {
        "1. Information": "Daily Prices (open, high, low, close) and Volumes",
        "2. Symbol": "NOK",
        "3. Last Refreshed": "2020-02-21",
        "4. Output Size": "Compact",
        "5. Time Zone": "US/Eastern"
    },
    "Time Series (Daily)": {
        "2020-02-21": {
            "1. open": "4.1400",
            "2. high": "4.1800",
            "3. low": "4.1100",
            "4. close": "4.1600",
            "5. volume": "21084879"
        },
        "2020-02-20": {
            "1. open": "4.1800",
            "2. high": "4.1900",
            "3. low": "4.1000",
            "4. close": "4.1600",
            "5. volume": "25559237"
        },
        ...

You should save a few stock data (AAPL, AMZN, NOK, TSLA) for a testing purposes to your server (for example – student.labranet.jamk.fi). Alpha Vantage is providing a free API service for their global community of users and recommend that you make API requests sparingly (up to 5 API requests per minute and 500 requests per day) to achieve the best server-side performance.

You can use also these:

Use Axios to load stocks data

Install axios to your project

npm install axios --save

Use axios in loadStock function to load stock data and display loaded data in browser’s inspector. Use your test data for testing. Remember use real API_KEY and URL when you are publishing your app!

const loadStock = (symbol) => {
  // use your own server for testing
  const URL_PATH = 'https://YOUR_SERVER/PATH/';
  const url = URL_PATH + symbol + '.json';

  // use in real case
  //const API_KEY = 'YOUR_API_KEY';
  //const URL_PATH = 'https://www.alphavantage.co/query?function=TIME_SERIES_DAILY';
  //const url = URL_PATH + '&symbol='+symbol+'&apikey='+API_KEY;

  // use axios to get data
  axios.get(url)
    .then(res => {
      console.log(res.data);
    })
}

You will might get CORS policy error. For security reasons, browsers restrict cross-origin HTTP requests initiated from scripts. For example, XMLHttpRequest and the Fetch API follows the same-origin policy. This means that a web application using those APIs can only request resources from the same origin the application was loaded from, unless the response from other origins includes the right CORS headers.

Now your application is running in localhost and it can’t load data from your own server by default.

You can run your local browser without web security when developing your app. So you can load data from the different servers.

Start your browsers in macOS:

open -a "Google Chrome" --args --disable-web-security --user-data-dir

Start your browsers in Windows:

"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --disable-web-security --user-data-dir="C:/ChromeDevSession"

You should now got loading working and you can continue developing your app in localhost.

Recharts

Install

Use some 3th party charting libraries for React to display time series stocks data. One good one is Recharts. Browser their site and learn how to use it.

Install Recharts to your project:

npm install recharts --save

Understand data

Get familiar with stock data returned by Alpha Vantage. It returns openhighlow and close price for the stock and the volume count.

"2020-02-21": {
    "1. open": "4.1400",
    "2. high": "4.1800",
    "3. low": "4.1100",
    "4. close": "4.1600",
    "5. volume": "21084879"
},
"2020-02-20": {
    "1. open": "4.1800",
    "2. high": "4.1900",
    "3. low": "4.1000",
    "4. close": "4.1600",
    "5. volume": "25559237"

LineChart

Use LineChart to show for example openclose and high data.

Import needed classes from recharts

// Rechart
import {
  LineChart, 
  Line, 
  XAxis, 
  YAxis, 
  CartesianGrid, 
  Tooltip, 
  Legend, 
  ResponsiveContainer
} from 'recharts';

Add a new React Hook to your App.

const [data, setData] = useState([]);

Modify loadStock function to modify loaded data to work with Rechart. Only 10 first time series data will be used with a following keys: dateopenhighlowclose and volume.

const loadStock = (symbol) => {

  ...

  // use axios to get data
  axios.get(url)
    .then(res => {
      // store loaded data to local variable
      var data = [];
      // take only 10 first
      var countMAX = 10;
      var count = 0;
      // loop keys stored in json like: "2020-02-20", "2020-02-21", etc...
      Object.keys(res.data["Time Series (Daily)"]).forEach(function(key) {
        // add count and take only countMAX 
        count++;
        if (count < countMAX) {
          // push loaded json data to array with keys: data, open, etc...
          data.push(
            {
              'date': key,
              'open': res.data["Time Series (Daily)"][key]["1. open"],
              'high': res.data["Time Series (Daily)"][key]["2. high"],
              'low': res.data["Time Series (Daily)"][key]["3. low"],
              'close': res.data["Time Series (Daily)"][key]["4. close"],
              'volume': res.data["Time Series (Daily)"][key]["5. volume"],
            }
          );
        }
      });
      setData(data);
    })
}

Render LineChart below FormControl in App’s rendering. Use ResponsiveContainer to get it sized correctly in mobile device’s screen. Learn other elements and attributes from Rechart’s documentation.

<div style={{width: '100%', height: '400px'}}>
  <ResponsiveContainer>
    <LineChart
      data={data}
      margin={{ top: 5, right: 30, left: 20, bottom: 5 }}
    >
      <CartesianGrid strokeDasharray="3 3" />
      <XAxis dataKey="date" />
      <YAxis domain={['dataMin', 'dataMax']}/>
      <Tooltip />
      <Legend />
      <Line type="monotone" dataKey="open" stroke="#8884d8" activeDot={{ r: 8 }} />
      <Line type="monotone" dataKey="close" stroke="#82ca9d" />
      <Line type="monotone" dataKey="high" stroke="#ff0000" />
    </LineChart>
  </ResponsiveContainer>
</div>

Test

Test your app in localhost. It should work now.

Remember

  • test with mobile size – use ‘Toggle device tool’
  • PWA test

Build and publish

Create a new build and publish your files to server. Remember use https connection. Test your application in your mobile phone browser (chrome@Android, safari@iOS). It should work now as it described at the start of this tutorial.

Add comment

Pasi Manninen

Pasi Manninen

Pasi is a mobile and web application developer. Currently working as a senior lecturer in JAMK University of Applied Sciences. Pasi has programming experience over many decades and has taught dozens of courses since the 90's.

Recent Posts

Follow Me