Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

React in Real-Time

DZone's Guide to

React in Real-Time

If you're interested in learning how to use React to build awesome web applications, read on to get quick overview of the entire process.

· Web Dev Zone
Free Resource

Tips, tricks and tools for creating your own data-driven app, brought to you in partnership with Qlik.

Single page applications frameworks (SPA) are at the center stage of modern web development. Top notch web applications depend on fast and responsive behavior in both user interface and data. Hence the popularity of frameworks like React, Angular, Vue, and Ember.

This blog post shows how you can get started with React and restdb.io for development of applications with real-time data.

React is an amazing piece of technology. With its real-time DOM manipulation and component thinking, it's just a perfect match for real-time enabled databases and frameworks like Firebase, RethinkDB, Pusher, and RestDB.io.

In this blog post, we'll be developing an application for displaying Nasdaq stock information.

The demo code is nothing fancy but focuses on showing how to set up up your React application and connecting it to a real-time data stream and a REST API.

Highlights

  • Standard web browser Eventsource API to connect to the restdb.io real time API.
  • Axios for communication with the REST API.
  • Dump of Nasdaq stock data stored in the database.
  • Deploy the finished application to a restdb.io server.

The screen shot below shows what the finished app looks like:

finished app

You can check out a live demo at this link.

Let's look at how this application is made.

Create the Project

Create a directory and install the wonderfully easy-to-use npm package create-react-app.

mkdir react-demo
cd react-demo/
npm install -g create-react-app
create-react-app realtime-react
cd realtime-react

Utilities

In addition to React, we need Axios for REST communication and Lodash for convenience.

# the -S option adds it to our package.json project file
npm install axios -S
npm install lodash -S

Create the Database

After creating a new database in restdb.io, we begin with importing data from a local CSV file downloaded from the web:

name,symbol,open,high,low,close,net chg,percent chg,volume,52 wk high,52 wk low,div,yield,P-E,ytd percent chg
1st Constitution Bancorp,FCCY,17.30,17.50,17.25,17.25,...,...,"2,900",20.85,11.83,0.20,1.16,15.83,-7.75
1st Source,SRCE,48.11,48.89,48.02,48.67,0.44,0.91,"31,238",50.77,31.88,0.76,1.56,21.07,8.98
2U,TWOU,45.23,46.37,45.08,46.24,1.30,2.89,"280,877",48.40,29.23,...,...,...,53.37
...

Because this data is from the Nasdaq stock exchange, we import the data into a new collection that we named nasdaq.

After the import, we can look at the data inside in the restdb.io data manager:

stock data in restdb.io

Yay! The data schema has been created automatically from the import data:

schema

This gives us the following REST endpoint for our stock data:

https://reactrealtime-6683.restdb.io/rest/nasdaq

Which will output a JSON document like the following:

{
    _id: "596335cd2b769915000007a6"
    name: "1st Constitution Bancorp"
    symbol: "FCCY"
    open: 17.3
    high: 17.5
    low: 17.25
    net chg: null
    percent chg: null
    volume: 2
    52 wk high: 20.85
    52 wk low: 11.83
    div: 0.2
    yield: 1.16
    P-E: 15.83
    ytd percent chg: -7.75
    price: 17.25
},
...

Secure CORS

In order to allow clients to communicate with our database, we must create a CORS api key with a connection profile. The secure CORS key is created in restdb.io under the database settings section.

The screen shot below shows the set up of both REST and Realtime:

cors

The settings above read like this:

  • Allow the client to send GET PUT REST API operations from * (any) domain.
  • Allow the client to listen for real-time events on POST PUT DELETE operations to :* (any) collection.

The database is now ready, and we can move on to create our React application.

Create the React Application

The application consists of 3 main source files (in addition to React files and HTML, CSS, etc.).

  • helper.js - global settings.
  • App.js - App component, Ticker component for stock list and real-time changes.
  • Autotrader.js - Trade component for sending emulated stock price changes to the database.

Global Settings

To keep things DRY we'll create a helper file helper.js which will contain our global application settings.

// helper.js
import axios from 'axios';

// CORS enabled apikey
const apikey = '5x5fx501xfcex9e87x11ex6x';

// Autotrade delay
const trade_delay = 10000; // millis

// REST endpoint
let restdb = axios.create({
    baseURL: 'https://reactrealtime-6683.restdb.io',
    timeout: 1000,
    headers: { 'x-apikey': apikey }
});
// Eventsource endpoint
const realtimeURL = `https://reactrealtime-6683.restdb.io/realtime?apikey=${apikey}`

export { apikey, restdb, realtimeURL, trade_delay };

Main App Component

In our App.js file we have 2 components, App and Tickers. App is the main component with the App container, and Tickers renders the stock list data and refreshes on real-time changes.

The code snippet below shows the main container of our application, a heading and two components, Trade and Tickers.

class App extends Component {
  componentDidMount() {
    // start trading
  }
  render() {
    return (
      <div className="App">
        <div className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <h2>React &amp; RestDB.io in Realtime</h2>
        </div>

        <h2>Nasdaq trading bots <small>fake</small></h2>
        <Trade/>
        <Tickers/>
      </div>
    );
  }
}

Connecting the Eventsource to a Realtime Stream

We can listen for real-time database changes via the browser EventSource API.

// connect to the realtime database stream
let eventSource = new EventSource(`https://reactrealtime-6683.restdb.io/realtime?apikey=${apikey}`);

To make sure the connection is valid you can view the live stream from your browser network inspector, shown in the screen shot below:inspect realtime stream

The inspector shows that we are receiving server events for putand ping. The putevents are changes in our nasdaqdata collection. And the ping events are keep-alive messages from the server. We will use this to make sure we can re-connect to the EventSource if/when the client looses connection to the server.

The trick is to store a time stamp for the last pingmessage. This code snippet shows how we add this to the component state:

// listen on ping from server, keep time
eventSource.addEventListener('ping', function(e) {
    this.setState(previousState => {
      return {ping: new Date(e.data)};
    });
}.bind(this), false);

We can now, in an interval, check how long time has passed since the client last got a ping message from the server. If that time span is too long (e.g. 20 sec), we re-connect our client.

// check if the realtime connection is dead, reload client if dead
setInterval(() => {
  let now = new Date().getTime();
  let diff = (now - this.state.ping.getTime()) / 1000;

  // haven't heard from the server in 20 secs?
  if (diff > 20) {
    // hard reload of client
    window.location.reload();
  }
}, 10000);

The Ticker Component

The Ticker component retrieves stock data from the database. It also listens for real-time events when data has changed.

// App.js
import React, { Component } from 'react';
import _ from 'lodash';
import logo from './logo.svg';
import './App.css';
import Trade from './Autotrader';
import { restdb, realtimeURL } from './helper.js';


class Tickers extends Component {
  constructor(props) {
    super(props);

    this.state = {ping: new Date(), evt: '', tickers: []};

    // connect to the realtime database stream
    let eventSource = new EventSource(realtimeURL);

    // check if the realtime connection is dead, reload client if dead
    setInterval(() => {
      let now = new Date().getTime();
      let diff = (now - this.state.ping.getTime()) / 1000;

      // haven't heard from the server in 20 secs?
      if (diff > 20) {
        // hard reload of client
        window.location.reload();
      }
    }, 10000);

    // listen on ping from server, keep time
    eventSource.addEventListener('ping', function(e) {
        this.setState(previousState => {
          return {ping: new Date(e.data)};
        });
    }.bind(this), false);

    // listen for database REST operations
    eventSource.addEventListener('put', (e) => {
        this.getTickerData()

    }, false);

  }

  // GET 20 stocks to display and trade
  getTickerData = () => {
    restdb.get("/rest/nasdaq?sort=symbol&max=20&metafields=true")
      .then(res => {
        let now = new Date().getTime();
        let tickers = res.data;
        let diff = null;

        // tag stocks that are changed in the last 10 secs
        _.each(tickers, (t) => {
          diff = (now - new Date(t._changed).getTime()) / 1000;
          if (diff < 10) {
            t.isChanged = true;
          } else {
            t.isChanged = false;
          }
        });

        this.setState(previousState => {
          return { tickers };
        });
      });
  }
  componentDidMount() {
    console.log("Start client");
    this.getTickerData();
  }

  render() {
    return (
      <div className="tickers">
        <table className="table">
          <thead>
            <tr>
              <th>Symbol</th>
              <th>Company</th>
              <th className="num">Price</th>
              <th className="num">Change</th>
            </tr>
          </thead>
          <tbody>

            {this.state.tickers.map(tic =>

              <tr key={tic._id} className={tic.isChanged ? (tic['net chg'] > 0 ? "up" : "down") : "passive"}> 
                <td>{tic.symbol}</td>
                <td>{tic.name}</td>
                <td className="num">{tic.price.toFixed(2) || 0.0}</td>
                <td className="num">{tic['net chg'] ? tic['net chg'].toFixed(2) : 0.0}</td>
              </tr>
            )}

          </tbody>
        </table>
      </div>
    );
  }
}

The Trading Component

Our last file, Autotrader.js, contains one component, Trade. The component sends updates (PUT) to a random stock from the list each 10th second.

import React, { Component } from 'react';
import { restdb, trade_delay } from './helper.js';

// emulate price change on stocks
class Trader extends Component {
    constructor(props) {
        super(props);
        this.state = {stocks: [], lasttrade: null};

        // GET 20 stocks
        restdb.get("/rest/nasdaq?sort=symbol&max=20")
        .then(res => {
            const stocks = res.data;
            this.setState(previousState => {
                return { stocks };
            });
        });

        // update a random stock each 10 sec
        setInterval(() => {
            let stockpos = Math.floor(Math.random() * 19);
            if (this.state.stocks[stockpos]) {
                let stockID = this.state.stocks[stockpos]._id;
                let newprice = (Math.random() * 1000.0) + 1.0;
                // PUT new price on one stock
                restdb.put(`/rest/nasdaq/${stockID}`,
                    {price: newprice}
                )
                .then((response) => {

                    this.setState(previousState => {
                        return {lasttrade: response.data};
                    });
                })
                .catch(function (error) {
                    console.log(error);
                });
            }
        }, trade_delay);
    }

    render() {
        return (
        <div className="autotrade">
            <p>Trading: {this.state.lasttrade ? (this.state.lasttrade.symbol + ' at ' + this.state.lasttrade.price.toFixed(2)) : 'none'}</p>
        </div>
        );
  }
}

export default Trader;

Server Codehook for Validation of Stock Price Change

Our final piece of code is for server side validation of PUT operations against our nasdaq database collection. We will create a database Codehook that triggers on beforePUT. This function validates that the new price if not negative, and it calculates the difference between the new and the old price. The difference is then stored in a property called ['net chg'].

// Codehooks for nasdaq
function beforePUT(req, res) {

    // fetch old value first
    let query = {_id: req.body._id};
    let hint = {};

    db.get("/rest/nasdaq", query, hint, function (err, result) {

        // set the delta value as old - new value
        req.body['net chg'] = req.body.price - result[0].price;
        req.body.price = Math.max(req.body.price, 0.0);
        // return the updated document
        res.end({"data": req.body});    
    });

}

This concludes our simple React application. The next step is to run and test it locally and then deploy it to the restdb.io server.

Run Application From a Local Node.js Server

If you install Node.js on your local machine you can take advantage of the rapid development cycle with hot code reloading and debugging. The create-react-app npm package has a command that spins up a Node server on our local machine.

# start a local node.js server
npm start

You should see something like this output in your terminal window:

npm start

You can now test your app from http://localhost:3000.

Build Application

To create a packaged application that can be deployed, you can use the handy build command.

npm run build

After the build you should see something similar to this:

Image title

Run Static File Server to Test Build

You can also test your deploy-ready package on a local HTTP server. Here we use the npm package serve:

serve -s build

You should see this output in the terminal window:

static server

You can now test you deployment app package from http://localhost:5000.

Our final step is to deploy our application to the world. We can deploy our React app to a variety of HTTP servers, but in this example, we'll be using the restdb.io server. Loading the app from the same server/domain that contains the data gives us a significant speed improvement.

Deploy Application to Restdb.io

restdb.io has a built in web server that lets you deploy and serve static files.

E.g. https://<yourdbname>.restdb.io/static

You can deploy to your database with the restdb-cli command line tool.

To deploy our React app, we run the following command:

restdb-cli --cmd upload --src ./build --dest /reactapp --database reactrealtime-6683 --apikey 
<your full access api-key here>

However, a much better approach is to add this as a deployment command to our application package.json file instead.

Also, adding relative paths to the build folders lets us deploy both locally and to a server with a different folder structure. We do this by setting the "homepage": "." property in the package.json file. Our final package.json file looks like this:

{
    "name": "realtime-react",
    "version": "0.1.0",
    "private": true,
    "homepage": ".",
    "dependencies": {
        "axios": "^0.16.2",
        "lodash": "^4.17.4",
        "react": "^15.6.1",
        "react-dom": "^15.6.1",
        "react-scripts": "1.0.10"
    },
    "scripts": {
        "start": "react-scripts start",
        "build": "react-scripts build",
        "test": "react-scripts test --env=jsdom",
        "eject": "react-scripts eject",
        "deploy": "restdb-cli --cmd upload --src ./build --dest /reactrealtime --database reactrealtime-6683 --apikey <your full access api-key here>"
    }
}

Let's run the deploy command.

npm run deploy

If all goes well, it should display something like this:

> realtime-react@0.1.0 deploy /Users/joneriksolheim/projects/react-demo/realtime-react
> restdb-cli --cmd upload --src ./build --dest /reactrealtime --database reactrealtime-6683 --apikey xxxxxxxxxxxxxxxxxxx


Upload successful!  3398298 total bytes uploaded

The deployed application is now served from the restdb.io server at this URL:

https://reactrealtime-6683.restdb.io/static/reactrealtime/build/index.html

Conclusion

Coding a real-time app with React is fun, and a lot easier than you might think. Simple state management with automatic DOM sync makes our code clean and small. Using the browsers standard EventSource API to listen for real-time data events are both simple and effective. And finally, using Axios for REST communication just rocks.

View full source code on GitHub here.

Explore data-driven apps with less coding and query writing, brought to you in partnership with Qlik.

Topics:
web dev ,react ,web application development ,javascript

Published at DZone with permission of Jon Erik Solheim, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}