Welcome to BoulderJS!

Building Platforms with React + WebSockets

What's React?

a JS library for building UI

What's a WebSocket?

a protocol for communication via channels

Normal Web Communication

visualization of the request / response cycle

WebSocket Communication

visualization of realtime communication

Why use them together?

providing new and engaging web experiences

https://alan-chat.herokuapp.com

https://sound-sockets.herokuapp.com

Putting the Pieces Together

https://github.com/alanbsmith/react-websocket-starter


    // ./server.js
    var path = require('path');
    var express = require('express');
    var app = express();
    var PORT = process.env.PORT || 8080;
    var server = require('http').createServer(app);
    var io = require('socket.io')(server);

    ...

    app.use(express.static(path.join(__dirname, 'dist')));

    app.get('/', function(request, response) {
      response.sendFile(__dirname + '/dist/index.html');
    });

    io.on('connection', function(client) {
      client.on('join', function(data) {
        console.log(data);
      });
    });

    server.listen(PORT);

Webpack Setup

./server.js

    
    ...

    // using webpack-dev-server and middleware in development environment
    if (process.env.NODE_ENV !== 'production') {
      var webpackDevMiddleware = require('webpack-dev-middleware');
      var webpackHotMiddleware = require('webpack-hot-middleware');
      var webpack = require('webpack');
      var config = require('./webpack.config');
      var compiler = webpack(config);

      app.use(webpackDevMiddleware(compiler, { 
        noInfo: true,
        publicPath: config.output.publicPath
      }));
      app.use(webpackHotMiddleware(compiler));
    }

    ...

    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8">
        <title>Chat</title>
      </head>
      <body>
        <div id='root'/>
      </body>
      <script src="/socket.io/socket.io.js"></script>
      <script src="/bundle.js"></script>
    </html>

./dist/index.html

View Setup


    import '../assets/stylesheets/base.scss';
    import React, { Component } from 'react';

    let socket = io.connect();

    const App = React.createClass({
      componentDidMount() {
        socket.on('connect', function(data) {
          socket.emit('join', 'hello world from the client!');
        });
      },

      render() {
        return (
          <h1>Hello, {this.props.name}!</h1>
        )
      }
    });

    export default App;

./src/components/App.js

Building our First Component

    ...

    const App = React.createClass({
      ...

      render() {
        return(
          <div> 
            <div className="header"></div>
            <div>
              <MessageList messages={this.state.messages} />
              <MessageForm />
            </div>
          </div>
        )
      }
   });

    export default App;

./src/components/App.js

Adding Functionality

import moment from 'moment';
let socket = io.connect();

const MessageForm = React.createClass({
  ...
  
  handleSubmit(e) {
    e.preventDefault();
    const text = this.refs.message.value.trim();
    const time = moment().format("h:mm a");
    this.refs.message.value = "";
    socket.emit('new-message', { author: this.state.name, text: text, display_time: time });
  },

  ...

  render() {
    return(
      <div className="footer">
        <form id="message-form" onSubmit={this.handleSubmit}>
          <input ref="message" type="text" placeholder="type your message here" />
        </form>
      </div>
    )
  }
});

export default MessageForm;

./src/components/MessageForm.js

Publishing Message Data


io.on('connection', function(client) {
  console.log('client connected!');

  client.on('join', function(data) {
    console.log(data);
  });

  client.on('new-message', function(data) {
    io.sockets.emit('add-message', data);
  });
});

./server.js

Listening on the Server

const App = React.createClass({
  getInitialState() {
    return { messages: [] };
  },

   ...
    
  componentDidMount() {
    socket.on('connect', function(data) {
      socket.emit('join', 'hello world from the client!');
    });
    socket.on('add-message', this._addMessage);
    this.getMessages();
  },

  _addMessage(data) {
    this.state.messages.push(data);
    this.setState({ messages: this.state.messages });
  },

});

./components/App.js

Updating the State

    
  ...

  client.on('new-message', function(data) {

    var promise = new Promise(function(resolve, reject) {
      var messageData = { message: data };
      requestClient.post('api/v1/messages', messageData, function(err, res, body) {
        if(err) {
          reject(err);
        }
        else {
          io.sockets.emit('add-message', body);
        }
      })
    });
  });
  
 ...

./server.js

Persisting Data

    
const Room = React.createClass({

  getInitialState() {
    return({
      userName: "",
      channelId: `channel-${this.props.params.id}`,
      name: "",
      messages: [],
    })
  },

  componentDidMount() {
    socket.emit('join', this.state.channelId );
    socket.on('new-message', this._addMessage);
    this.getBoardData();
  },

  ...

./src/components/Room.js

Adding Channels

    
  ...

  client.on('join', function(channel) {
    client.join(channel)
  });

  ...

  client.on('new-message', function(data) {

    var promise = new Promise(function(resolve, reject) {
      var messageData = { message: data };
      requestClient.post('api/v1/messages', messageData, function(err, res, body) {
        if(err) {
          reject(err);
        }
        else {
          io.sockets.to(data.channelId).emit('new-message', body);
        }
      })
    });
  });
  
 ...

./server.js

Adding Channels

https://github.com/alanbsmith/react-websocket-starter

Sharing!

Thanks!

Alan Smith

@_alanbsmith

github.com/alanbsmith

React + WebSockets

By Alan Smith

React + WebSockets

  • 378