Wednesday, August 3, 2016

Using JSX and React_part 2 (end)

We will also need some additional packages, and for that we will use bower.

Create a bower.json and add the following:

  1. {
  2.   "name": "react-webrtc-chat",
  3.   "main": "app.js",
  4.   "version": "0.0.0",
  5.   "ignore": [
  6.     "**/.*",
  7.     "node_modules",
  8.     "bower_components",
  9.     "public/lib",
  10.     "test",
  11.     "tests"
  12.   ],
  13.   "dependencies": {
  14.     "react": "~0.12.2",
  15.     "jquery": "~2.1.4",
  16.     "eventEmitter": "~4.2.7",
  17.     "peerjs": "~0.3.14",
  18.     "bootstrap": "~3.3.5"
  19.   }
  20. }

With this configuration, we bring down reactjQuerybootstrapeventEmitter for firing events and the peerJS Client library for wrapping the WebRTC.

Finally, specify where to install this by setting the .bowerrc file:

  1. {
  2.   "directory": "src/lib"
  3. }

From here, just sit back and install your dependencies via:


  1. $ bower install && npm install

Once this command has finished, you will see a new directory node_modules and src/lib. These contain the modules ready for use.

Now create an app.js in the main directory alongside your package.json, etc. This will be the main entry point of your application

  1. //Configure our Services
  2. var express = require('express'),
  3.     PeerServer = require('peer').PeerServer,
  4.     events = require('./src/Events.js'),
  5.     app = express(),
  6.     port = process.env.PORT || 3001;
  7.  
  8. //Tell express to use the 'src' directory
  9. app.use(express.static(__dirname + '/src'));
  10.  
  11. //Configure the http server and PeerJS Server
  12. var expressServer = app.listen(port);
  13. var io = require('socket.io').listen(expressServer);
  14. var peer = new PeerServer({ port: 9000, path: '/chat' });
  15.  
  16. //Print some console output
  17. console.log('#### -- Server Running -- ####');
  18. console.log('Listening on port', port);
  19.  
  20. peer.on('connection', function (id) {
  21.   io.emit(events.CONNECT, id);
  22.   console.log('# Connected', id);
  23. });
  24.  
  25. peer.on('disconnect', function (id) {
  26.   io.emit(events.DISCONNECT, id);
  27.   console.log('# Disconnected', id);
  28. });

This will simply create an Express server, making the src/ files we just got with bower now accessible via HTTP. Then a socket.io instance was created to listen on the expressServer object. This is used for polling and facilitating the next step for the PeerServer, which will actually do the WebRTC chat part.

To configure a PeerServer, all you need to do is specify the port and path the server will run on, and then start configuring events with the .on method. We are using a separate file named Events.js to specify our app’s events.

  1. peer.on('connection', function (id) {
  2.   io.emit(events.CONNECT, id);
  3.   console.log('# Connected', id);
  4. });

Here we use the events.CONNECT event to specify when a user has connected to our app. This will be used by the states of our view components to update their displays in real time.

To do this, we need to create the Server for our peer-to-peer connections to proxy through.

Create a file in src/Server.js and add the following:

  1. /* global EventEmitter, events, io, Peer */
  2. 'use strict';
  3.  
  4. function ChatServer() {
  5.   EventEmitter.call(this);
  6.   this._peers = {};
  7. }
  8.  
  9. ChatServer.prototype = Object.create(EventEmitter.prototype);
  10.  
  11. ChatServer.prototype.onMessage = function (cb) {
  12.   this.addListener(events.MSG, cb);
  13. };
  14.  
  15. ChatServer.prototype.getUsername = function () {
  16.   return this._username;
  17. };
  18.  
  19. ChatServer.prototype.setUsername = function (username) {
  20.   this._username = username;
  21. };
  22.  
  23. ChatServer.prototype.onUserConnected = function (cb) {
  24.   this.addListener(events.CONNECT, cb);
  25. };
  26.  
  27. ChatServer.prototype.onUserDisconnected = function (cb) {
  28.   this.addListener(events.DISCONNECT, cb);
  29. };
  30.  
  31. ChatServer.prototype.send = function (user, message) {
  32.   this._peers[user].send(message);
  33. };
  34.  
  35. ChatServer.prototype.broadcast = function (msg) {
  36.   for (var peer in this._peers) {
  37.     this.send(peer, msg);
  38.   }
  39. };
  40.  
  41. ChatServer.prototype.connect = function (username) {
  42.   var self = this;
  43.   this.setUsername(username);
  44.   this.socket = io();
  45.   this.socket.on('connect', function () {
  46.     self.socket.on(events.CONNECT, function (userId) {
  47.       if (userId === self.getUsername()) {
  48.         return;
  49.       }
  50.       self._connectTo(userId);
  51.       self.emit(events.CONNECT, userId);
  52.       console.log('User connected', userId);
  53.     });
  54.     self.socket.on(events.DISCONNECT, function (userId) {
  55.       if (userId === self.getUsername()) {
  56.         return;
  57.       }
  58.       self._disconnectFrom(userId);
  59.       self.emit(events.DISCONNECT, userId);
  60.       console.log('User disconnected', userId);
  61.     });
  62.   });
  63.   console.log('Connecting with username', username);
  64.   this.peer = new Peer(username, {
  65.     host: location.hostname,
  66.     port: 9000,
  67.     path: '/chat'
  68.   });
  69.   this.peer.on('open', function (userId) {
  70.     self.setUsername(userId);
  71.   });
  72.   this.peer.on('connection', function (conn) {
  73.     self._registerPeer(conn.peer, conn);
  74.     self.emit(events.CONNECT, conn.peer);
  75.   });
  76. };
  77.  
  78. ChatServer.prototype._connectTo = function (username) {
  79.   var conn = this.peer.connect(username);
  80.   conn.on('open', function () {
  81.     this._registerPeer(username, conn);
  82.   }.bind(this));
  83. };
  84.  
  85. ChatServer.prototype._registerPeer = function (username, conn) {
  86.   console.log('Registering', username);
  87.   this._peers[username] = conn;
  88.   conn.on('data', function (msg) {
  89.     console.log('Message received', msg);
  90.     this.emit(events.MSG, { content: msg, author: username });
  91.   }.bind(this));
  92. };
  93.  
  94. ChatServer.prototype._disconnectFrom = function (username) {
  95.   delete this._peers[username];
  96. };

This is the main guts of the application. Here we configure the ChatServer object with all of its functions.

First we use socket.io to establish signalling of a new user connected via the events.CONNECT as so:

  1. ChatServer.prototype.connect = function (username) {
  2.   var self = this;
  3.   this.setUsername(username);
  4.   this.socket = io();
  5.   this.socket.on('connect', function () {
  6.     self.socket.on(events.CONNECT, function (userId) {
  7.       if (userId === self.getUsername()) {
  8.         return;
  9.       }
  10.       self._connectTo(userId);
  11.       self.emit(events.CONNECT, userId);
  12.       console.log('User connected', userId);
  13.     });

Then, to connect to the PeerServer, we use the following:

  1. this.peer = new Peer(username, {
  2.     host: location.hostname,
  3.     port: 9000,
  4.     path: '/chat'
  5.   });

We then listen for events via the on method:this.peer.on('open', function (userId) {

  1.     self.setUsername(userId);
  2.  });
  3. this.peer.on('connection', function (conn) {
  4.     self._registerPeer(conn.peer, conn);
  5.     self.emit(events.CONNECT, conn.peer);
  6.  });

We also have our JSX inside components in the components/chat directory. Take your time to look through all of them in the repository. I will be focusing on the ChatBox component for now:

  1. /** @jsx React.DOM */
  2.  
  3. 'use strict';
  4.  
  5. var ChatBox = React.createClass({
  6.  
  7.   getInitialState: function () {
  8.     return { users: [] };
  9.   },
  10.  
  11.   componentDidMount: function () {
  12.     this.chatProxy = this.https://school.codequs.com/p/SkRTHvU_props.chatProxy;
  13.     this.chatProxy.connect(this.props.username);
  14.     this.chatProxy.onMessage(this.addMessage.bind(this));
  15.     this.chatProxy.onUserConnected(this.userConnected.bind(this));
  16.     this.chatProxy.onUserDisconnected(this.userDisconnected.bind(this));
  17.   },
  18.  
  19.   userConnected: function (user) {
  20.     var users = this.state.users;
  21.     users.push(user);
  22.     this.setState({
  23.       users: users
  24.     });
  25.   },
  26.  
  27.   userDisconnected: function (user) {
  28.     var users = this.state.users;
  29.     users.splice(users.indexOf(user), 1);
  30.     this.setState({
  31.       users: users
  32.     });
  33.   },
  34.  
  35.   messageHandler: function (message) {
  36.     message = this.refs.messageInput.getDOMNode().value;
  37.     this.addMessage({
  38.       content: message,
  39.       author : this.chatProxy.getUsername()
  40.     });
  41.     this.chatProxy.broadcast(message);
  42.   },
  43.  
  44.   addMessage: function (message) {
  45.     if (message) {
  46.       message.date = new Date();
  47.       this.refs.messagesList.addMessage(message);
  48.     }
  49.   },
  50.  
  51.   render: function () {
  52.     return (
  53.       <div className="chat-box" ref="root">
  54.         <div className="chat-header ui-widget-header">React p2p Web RTC Chat</div>
  55.         <div className="chat-content-wrapper row">
  56.           <UsersList users={this.state.users} username={this.props.username} ref="usersList"></UsersList>
  57.           <MessagesList ref="messagesList"></MessagesList>          
  58.         </div>
  59.         <MessageInput
  60.           ref="messageInput"
  61.           messageHandler={this.messageHandler}>
  62.         </MessageInput>
  63.       </div>
  64.     );
  65.   }
  66. });

This class takes usage of the ChatServer we created before, utilising it as a proxy for the ChatBox component.

The components and libraries are finally all rendered on the page with index.html and served via node express.

To start the app, run npm start and point your browser to http://localhost:3001 to take a look at the chat.

Going Live on Heroku
Serving from the cloud is really easy with Heroku. Sign up for a free account and then you can install the heroku toolbelt on to your system. Read more about getting Heroku set up at the Heroku Dev Center.

Now that you have Heroku available, log in and create a new project like so:

  1. $ git clone git@github.com:tomtom87/react-p2p-chat.git
  2. $ cd react-p2p-chat
  3. $ heroku create
  4. Creating sharp-rain-871... done, stack is cedar-14
  5. http://sharp-rain-871.herokuapp.com/ | https://git.heroku.com/sharp-rain-871.git
  6. Git remote heroku added

Here you will get a random name from Heroku and your app URL—in our example it is http://sharp-rain-871.herokuapp.com/. Heroku also creates a git repo for this app.

Now it’s as simple as pushing your code to heroku:

  1. $ git push heroku master

Once the push is finished, you will be able to start your web service with the following:

  1. $ heroku ps:scale web=1

Now just visit the URL provided, or as a shortcut use the open command as so:

  1. $ heroku open

Conclusions

You’ve learned how to create JSX components and interface them with React, with a detailed example of the chat application. Take some time to go through the code and see how React and the components/chat directory are working.

Combined with the deployment to Heroku, you can start hacking and creating your own React apps for the cloud!
Written by Tom Whitbread
If you found this post interesting, follow and support us.
Suggest for you:

Build Web Apps with React JS and Flux

Meteor and React for Realtime Apps

Modern React with Redux

Build Apps with React Native

The Complete Web Development Tutorial Using React and Redux


No comments:

Post a Comment