Making ReactJS Realtime With Websockets

making-reactjs-realtime-with-websockets-header.png

In this tutorial, I will be showing you how to build a chat application using ReactJS and Pusher. T

Introduction

In this tutorial, I will be showing you how to build a chat application using ReactJS and Pusher. This is a very simple application and it comes with functionality which will be showing some of the rich features of Pusher and how you can easily combine it with a modern library like React.

Every chat application requires realtime functionality. This amongst other benefits allows interaction between users in a different location(s). A chat application like this can be built to function on diverse platforms, be it mobile or web applications.

Pusher is a platform that allows developers to easily build an application with realtime features as quickly as possible. Pusher specializes in building realtime and scalable infrastructures for developers and is packaged with powerful features like client events, queryable API, Pub/Sub messaging and others.

We will explore Pusher as we proceed in this tutorial and leverage on the DOM manipulation, event-driven and data-binding ability of React.

A basic knowledge of JavaScript and React will be of advantage so as to effectively participate in this tutorial.

A sneak peek into what we will build in this tutorial:

Getting Started

For a hitch-free flow of procedures in this tutorial, we will begin by setting up the required credentials with Pusher. If you don’t have an account, kindly create one. Once you are done, go ahead and create a new app from your dashboard. Don’t forget to take note of your app_id, key, secret and cluster as you will be required to use them later in this tutorial.

Create React-app

To quickly scaffold a React app, we will make use of the create``-react-app tool. This gives us easy access to the CLI tool that will be used to start building our chat application.

It is important that you have Node and npm installed on your machine. Quickly follow this link to complete that, if you don’t have it already. To verify if you have Node and npm installed, open up the terminal and run the command below:

1npm -v 
2    node -v

The version for each one will be displayed, if they are installed. The latest versions are fine.

Installation

Now, install create-react-app and also scaffold a new React app with the following commands:

1npm install -g create-react-app
2
3    create-react-app react-pusher

Once all the necessary files are installed, change directory into react-``pusher and start the application with:

1npm start

By now, you should have a new tab opened in your default browser:

We have successfully set up the front-end of the application, we will return to this very soon. Let’s proceed to set up the server application.

Node Server

Node.js using the Express web framework will be used as our server application. The server application, amongst other functions, will provide endpoints to send messages for our chat app so as to ensure interaction between users. There is a number of dependencies required for the application server, so let’s install them immediately:

1npm install --save axios body-parser cors express pusher pusher-js

To configure the entry point of the application, create a file called server.js and paste the code below into it:

1const Pusher = require('pusher');
2    const express = require('express');
3    const bodyParser = require('body-parser');
4    const cors = require('cors');
5
6    const app = express();
7
8    app.use(cors());
9    app.use(bodyParser.urlencoded({extended: false}));
10    app.use(bodyParser.json());
11    const pusher = new Pusher({
12      appId: 'APP_ID',
13      key: 'APP_KEY',
14      secret: 'APP_SECRET',
15      cluster: 'APP_CLUSTER',
16      encrypted: true
17    });
18    app.set('PORT', process.env.PORT || 5000);
19
20    app.post('/message', (req, res) => {
21      const payload = req.body;
22      pusher.trigger('chat', 'message', payload);
23      res.send(payload)
24    });
25
26    app.listen(app.get('PORT'), () => 
27      console.log('Listening at ' + app.get('PORT')))

Here we initialized Pusher with the required credentials and then created an endpoint to process messages from the frontend of our application.

Building Components

To define a good application structure and fully see our chat app in action, let’s create some components. In React, components can either be stateful or stateless depending on the functionality that it was created for. The common pattern is to have stated for container components only and pass those states to UI components as props.

Create components from the terminal with:

1touch ChatList.js ChatBox.js

and lastly their respective stylesheet:

1touch ChatList.css ChatBox.css

Each of the components’ logic needs to be created. Let’s do that now!

ChatBox.js

This component contains the input field for accepting the users’ message and also displays a welcome message showing the username passed in from the parent component. Furthermore, handleTextChange is also passed from the parent component. This component is exported and configured like:

1import React from "react";
2    import './ChatBox.css';
3    export default ({ text, username, handleTextChange }) => (
4      <div>
5        <div className="row">
6        <div className="col-xs-12">
7          <div className="chat">
8            <div className="col-xs-5 col-xs-offset-3">
9              <input
10                type="text"
11                value={text}
12                placeholder="chat here..."
13                className="form-control"
14                onChange={handleTextChange}
15                onKeyDown={handleTextChange}
16              />
17            </div>
18            <div className="clearfix"></div>
19          </div>
20        </div>
21        <h4 className="greetings">Hello, {username}</h4>
22      </div>
23    </div>
24    );

This component above is styled with imported stylesheet ChatBox.css :

1.greetings {
2        margin-top: 90px;
3        text-align: center;
4    }

ChatList.js

This is responsible for displaying the list of chats inputted by each user. Added to it are the username and messages. This is also implemented as shown below and exported:

1import React from "react";
2    import "./ChatList.css";
3    import avatar from "./avatar.png";
4    export default ({ chats }) => (
5      <ul>
6        {chats.map(chat => {
7          return (
8            <div>
9              <div className="row show-grid">
10                <div className="col-xs-12">
11
12                  <div className="chatMessage">
13                    <div key={chat.id} className="box">
14                      <p>
15                        <strong>{chat.username}</strong>
16                      </p>
17                      <p>{chat.message}</p>
18                    </div>
19                    <div className="imageHolder">
20                    <img src={avatar} className="img-responsive avatar" alt="logo" />
21                  </div>
22                  </div>
23                </div>
24              </div>
25            </div>
26          );
27        })}
28      </ul>
29    );

And the stylesheet ChatList.css:

1ul {
2      list-style-type: none;
3    }
4    .chat {
5      margin-top: 50px;
6    }
7    .chatMessage {
8      position: relative;
9      border: 1px solid #ccc;
10      border-radius: 5px;
11      margin: 20px auto;
12      width: 700px;
13      display: table;
14    }
15    .box {
16      background-color: #ffffff;
17      padding: 20px;
18      border-bottom: 1px solid #cccccc;
19    }
20    .imageHolder {
21      width: 50px;
22      height: 50px;
23      position: absolute;
24      top: 20px;
25      right: 50px;
26    }

We have successfully completed the ChatBox and ChatList component. As far as the application is concerned, these modules are not yet available to be rendered in the parent app component yet. To correct this, the A``pp.js file needs to be configured.

Parent Container Component (App.js)

This is regarded as the parent component. It ensures effective parent-child communication by passing props down to each child component. In App.js, a class named App is used to extend the React Component class and a constructor is defined with its properties and methods. In App.js required node modules and CSS file are imported with:

1import React, { Component } from 'react';
2    import axios from 'axios';
3    import Pusher from 'pusher-js';
4    import ChatList from './ChatList';
5    import ChatBox from './ChatBox';
6    import logo from './logo.svg';
7    import './App.css';
8    ...

Next, a class that extends the Component class imported from the React module is created. Also defined in this class is the state variables. The super() method is also called whenever a child class is extended from a parent class. In this case, props are also passed as a parameter. The state variables in the app are created and defined in the constructor like this:

1...
2    class App extends Component {
3      constructor(props) {
4        super(props);
5        this.state = {
6          text: '',
7          username: '',
8          chats: []
9        };
10      }
11      componentDidMount() {
12        ...
13      }
14      handleTextChange(e) {
15       ...
16      }
17      render() {
18        return (
19          <div className="App">
20           ...
21          </div>
22        );
23      }
24    }
25    ...

After defining the required state of the app, you can start subscribing to the Pusher channel and binding to the events emitted by your server:

1componentDidMount() {
2        const username = window.prompt('Username: ', 'Anonymous');
3        this.setState({ username });
4        const pusher = new Pusher('APP_KEY', {
5          cluster: 'APP_CLUSTER',
6          encrypted: true
7        });
8        const channel = pusher.subscribe('chat');
9        channel.bind('message', data => {
10          this.setState({ chats: [...this.state.chats, data], test: '' });
11        });
12        this.handleTextChange = this.handleTextChange.bind(this);
13      }
14
15      handleTextChange(e) {
16        if (e.keyCode === 13) {
17          const payload = {
18            username: this.state.username,
19            message: this.state.text
20          };
21          axios.post('http://localhost:5000/message', payload);
22        } else {
23          this.setState({ text: e.target.value });
24        }
25      }

Bringing it all together:

1render() {
2        return (
3          <div className="App">
4            <header className="App-header">
5              <img src={logo} className="App-logo" alt="logo" />
6              <h1 className="App-title">Welcome to React-Pusher Chat</h1>
7            </header>
8            <section>
9              <ChatList chats={this.state.chats} />
10              <ChatBox
11                text={this.state.text}
12                username={this.state.username}
13                handleTextChange={this.handleTextChange}
14              />
15            </section>
16          </div>
17        );
18      }

In case you missed anything, you can find the complete App.js file right here.

Conclusion

This article has covered the basic steps necessary to create a realtime chat application using React and Pusher. Moving forward, the concept demonstrated here can be built upon and extended to cater for any other realtime features you wish to build in your application. I hope you have found this tutorial helpful and feel free to improve on the code available here and drop a comment.