React Tutorial: Getting started with React, Express and Socket.io

November 18, 2015
Written by
Sam Agnew
Twilion

twilio_react

I’ve been having a lot of fun messing around with React lately. For example, you might have seen a post I wrote on how to get set up with React, Webpack and Babel for ES6 code. Creating that post was a blast, but I didn’t want the fun to stop there so I kept on building. In this post, I’ll show you how to use React to build reusable, composable components for displaying and maintaining the state of your data in a real application.

Tracking the status of a phone call is a great example to show how React deals with handling state. To do this, let’s build a dashboard that will monitor phone calls using call progress events for Twilio Voice. You can find the code for the backend portion of this dashboard here if you want to focus on just building the client side React app for now.

The back end uses Express to receive requests from Twilio when a call’s status changes as well as to serve our React app. Socket.IO is used to communicate between the client and server in real time.

If you want to learn how the back end was built step by step you can check out these resources:

  • The small app built for tracking call progress events came from my previous blog post.
  • Socket.IO is used to sync the data between client and server. You can read this blog post to find out how to add Socket.IO to our server side app.
  • You can follow through my set up post to make sure you have Webpack and Babel working.

Getting ready to pick up the phone

In order to run the code powering our call monitoring app, you are going to need Node.js and npm installed. At the time of writing this, I have Node v4.0 and npm v2.10.1 installed.

You will also need to create a Twilio account and purchase a phone number with Voice capabilities. You can get through this post using a trial account, but you will have to verify each phone number that you make calls to with the API. Upgrading your account will allow you to make calls without verifying the phone number first.

Set up the dashboard’s backend locally by entering the following commands into your terminal. You may have to type your GitHub credentials:

git clone https://github.com/sagnew/CallStatusDashboard.git
cd CallStatusDashboard/
git checkout backend-with-socketio
npm install

You can check to make sure everything is working by running node index.js in your terminal and visiting http://localhost:3000. You will see by looking at the console in your terminal that you’ve made a socket connection.

Now that all of the dependencies installed to work with the existing version of this app are taken care of, we need to install react for building components and react-dom for rendering them to the DOM:

npm install —save react
npm install —save react-dom

Building our React components

Described by its own homepage as the V in MVC, React allows you to build reusable UI components and makes maintaining changes in your data’s state effortless by abstracting the browser DOM.

For our call state monitoring dashboard, there will be two React components to keep track of the phone calls. One will be a parent component containing a list of all phone calls, and the other will be for displaying the data of each individual phone call.

The parent component will hold an array of all phone calls, and whenever the status of a call changes, this will be updated in our array and passed down to the individual component as a property. When working with React components, it’s best to have the parent-most component hold the actual data and pass it down as properties to its children.

Let’s start by creating a component for a single phone call. In the project’s static directory, open a file called PhoneCall.jsx and add the following code:

import React from 'react';

class PhoneCall extends React.Component {
  render() {
    return (
      <div className="phone-call">
        <h4 className="call-SID">
          {"Call SID: " + this.props.callSid}
        </h4>
        <h4 className="to-number">
          {"To: " + this.props.to}
        </h4>
        <h4 className="from-number">
          {"From: " + this.props.fromNumber}
        </h4>
        <h4 className="date-updated">
          {"Call Status: " + this.props.callStatus}
        </h4>
      </div>
    );
  }
};

export default PhoneCall;

This allows us to lay out how we want the data of one phone call to look when it is rendered. Notice that this is an ES6 class definition, which inherits its prototype directly from React.Component so that it is no longer necessary to call React.createClass.

We aren’t finished yet, but let’s make sure everything is working by rendering our component with some dummy data. Add two more lines to the end of static/PhoneCall.jsx:

import ReactDOM from 'react-dom';
ReactDOM.render(<PhoneCall callSid='123456789' to='+19999999999' fromNumber='+18888888888' callStatus='completed'/>, document.getElementById('phone-calls'));

The ReactDOM library can mount React components to specific DOM elements. In this example a PhoneCall component with dummy data is mounted to a div on the page to make sure everything’s working.

Now before everything can be rendered, Webpack needs an entry point to our JavaScript in order to bundle everything for the browser. This should sound familiar if you’ve followed through my previous post on setting up React. Open a file called bundleEntry.js and add the following line:

import PhoneCall from './static/PhoneCall.jsx';

Don’t forget to make sure the bundle.js file generated by Webpack is included in your HTML, like we did before.

Now compile everything with Webpack, and run the server so we can see our dummy data rendered to a web page. You can do this by running the start script:

npm start

When you visit http://localhost:3000 you should see the following on the web page:

Screen Shot 2015-10-27 at 11.56.38 AM.png

You can feel free to remove those last two lines of static/PhoneCall.jsx as we will add code for rendering our components in a different place later.

Creating a parent component to hold the data

Now let’s move on to handling the state of all phone calls. Every time the status of a call changes, the item in the list representing that call will be updated, and then the state of the parent component will be updated to contain the new list. This way whenever a call is created it will be added to our dashboard.

Create the container component to hold all of the phone calls that are taking place. Open a file in your static directory called PhoneCallBox.jsx and add the following code:

import React from 'react';
import ReactDOM from 'react-dom';
import PhoneCall from './PhoneCall.jsx';

const socket = io();

class PhoneCallBox extends React.Component {

  constructor() {
    super();
    this.state = {phoneCalls: []}
  }

  render() {
    let phoneCalls = this.state.phoneCalls.map((call) => {
     return <li> <PhoneCall to={call.to} fromNumber={call.fromNumber} callSid={call.callSid} callStatus={call.callStatus}/> </li>
    });

    return (
      <div className="phoneCallBox">
        <h1>Phone calls</h1>
        <div className="phoneCallList">
          {phoneCalls}
        </div>
      </div>
    );
  }
}

ReactDOM.render(<PhoneCallBox/>, document.getElementById('phone-calls'));

You’ll notice that the class contains a constructor defining the initial state of our data and a render function defining how the data looks when rendered. There is also a call to ReactDOM.render in order to mount this component to our phone-calls element.

PhoneCallBox’s render function maps each phone call object in our array to a PhoneCall React component wrapped in an

  • . The entire list of phone calls is then rendered.

    The only problem right now is that there is no way to handle changes to a call’s status. This is where we can add onto the previous code from my Socket.IO post by creating a listener for status updates sent from the server.

    Modifying the parent component to handle state changes

    To handle the state changes we need to create a function that will execute whenever a call’s status is updated. This function should receive an object from the server defining the new state of a phone call, and iterate through the phoneCalls array to update that call’s status.

    Open PhoneCallBox.jsx in your static directory once again and add this function to the PhoneCallBox React class:


import React from 'react';
import ReactDOM from 'react-dom';
import PhoneCall from './PhoneCall.jsx';

const socket = io();

class PhoneCallBox extends React.Component {

  constructor() {
    super();
    this.state = {phoneCalls: []}
  }

  handleStateChange(newCallState) {

    let isNewCall = true;
    let phoneCalls = this.state.phoneCalls.map((call) => {
      if (call.callSid === newCallState.callSid) {
        // This is the updated phone call.
        isNewCall = false;
        return newCallState;
      } else {
        // This is an unchanged phone call.
        return call;
      }
    });

    if(isNewCall) {
      phoneCalls.push(newCallState);
    }

    this.setState({phoneCalls});
  }

  render() {
    let phoneCalls = this.state.phoneCalls.map((call) => {
      return <li> <PhoneCall to={call.to} fromNumber={call.fromNumber} callSid={call.callSid} callStatus={call.callStatus}/> </li>
    });

    return (
      <div className="phoneCallBox">
        <h1>Phone calls</h1>
        <div className="phoneCallList">
          {phoneCalls}
        </div>
      </div>
    );
  }
}

ReactDOM.render(<PhoneCallBox/>, document.getElementById('phone-calls'));

Now that we have a function to use as a callback for handling state changes, we need to connect the client’s rendering to some actual data coming from the server.

Hop back into PhoneCallBox.jsx and add a Socket.IO event listener to our React component’s constructor function so the handleStateChange function is bound to “call progress event” events before the component mounts:


  constructor() {
    super();
    this.state = {phoneCalls: []}
    socket.on('call progress event', (newCallState) => this.handleStateChange(newCallState));
  }

Now update the entry point for Webpack to bundle all of the client side code. Open up bundleEntry.js again and import the parent PhoneCallBox React component instead:

import PhoneCallBox from './static/PhoneCallBox.jsx';

Now to bundle everything with webpack and run the Express server one more time, use the start script defined in package.json:

npm start

Here is where you’ll need your Twilio account and a phone number with voice capabilities. You’ll need to set these environment variables so you can make phone calls using your Twilio account to test the dashboard.

export TWILIO_ACCOUNT_SID='YOUR-ACCOUNT-SID'
export TWILIO_AUTH_TOKEN='YOUR-AUTH-TOKEN'

In order for Twilio to see your web app, you’ll need to have ngrok running on port 3000. To do that you can check out the first part of this awesome post.

Edit makeCall.js to have all of the right values and visit http://localhost:3000 one more time.

Now fire up some phone calls with makeCall.js to see changes on the page. Feel free to run this script several times while still on the page:

node makeCall.js

phonecallssocketio.png

Don’t hang up the phone just yet

With this code, the dashboard app that was started in the call progress events post is complete from front to back. We have an Express server that receives call progress events when a phone call’s state changes, a websockets layer using Socket.IO to communicate changes between client and server, and a ReactJS-based front end for displaying and handling the data’s state in the view.

Using this as a basic example of what you can do with React, you are ready to start building more.

Try continuing this project by adding a persistent datastore to keep track of calls over time or perhaps by implementing an interface on the client side to control the flow of calls(I.E. hanging up a phone call by clicking a button).

I’d love to see what cool things you build. Feel free to reach out if you have any questions.