SuperPumpup (dot com)
General awesomeness may be found here.

12 August 2014

ReactJS and Vert.x

I'm building a new project in Vert.x and it's amazing to be able to separate concerns well. However, I "miss" having a Rails project to fall back on.

The first iteration of this project looked like:

Rails-centric Connectivity

A big Rails that was the default "catch-all" for complexity, and then things would kind of get extracted out into backing services. Everything communicated through RabbitMQ and though I got it pretty well-configured (running Docker on CoreOS with discovery and fun things like that), it seemed pretty fragile and VERY complex. So many different technologies.

With Vert.x, I can build the same project like this:

Vert.x Connectivity

Where there is a single API to publish (or send) messages on the Event Bus, you can link the Event Bus across nodes, and making a new service has about the same overhead (code, config management, etc) as making a new Ruby class. Plus, all the messaging is part of the framework, so it's just 'there'.

Also, there are really only (at this point) two different technologies. My application code (whatever language different parts of the code are in, they're still my application), and Vert.x. Beautiful.

But I lost the old fallback of Rails. As I looked, a lot of people are using Vert.x with thick Javascript client front ends, since you can actually hook the browsers directly to the Event Bus. So in each connected client app, you can watch for events named, say, "markets.6.order_book_summary" and every time a service publishes a message with that "subject", it will get broadcast to all the clients for "free".

Of the thick Javascript frontends, Angular seems to be the most-commonly-used. I've had some experience with Angular and thought it was fine. However, a good (and very respected) friend insists that the worst things in the world are, in order:

  • Nazis
  • Angular
  • JQuery
  • Truffle oil

Let's Explore ReactJS

So the point of this is that I wanted to try building a ReactJS tutorial in a Vert.x verticle and get them "wired up". The starting point is the end of this tutorial:

http://facebook.github.io/react/docs/tutorial.html

And the files you need are:

https://github.com/reactjs/react-tutorial/blob/master/index.html

https://github.com/reactjs/react-tutorial/blob/master/scripts/example.js

The Vert.x backend will work if it looks like this:

# web_server_verticle.rb
require "vertx"

include Vertx

logger = Vertx.logger
port_number = 8080

logger.info "Webapp loading, port #{port_number}..."

server = Vertx::HttpServer.new

# This web server will simple serve files out of the web/ directory relative to this file
server.request_handler do |req|
  file = ''
  logger.debug(req)
  if req.path == '/'
    file = 'index.html'
  elsif !req.path.include?('..')
    file = req.path
  end
  req.response.send_file('./web/' + file)
end

sockJSServer = Vertx::SockJSServer.new(server)

# EventBus messages with the address of comments.create_comments and comments.get_comments will 
# be allowed inbound from the browser.  comments.all_comments will be forwarded from 
# the bus to all connected clients

sockJSServer.bridge({'prefix' => '/eventbus'},
  [
    { address: 'comments.create_comment'},
    { address: 'comments.get_comments'}
  ],
  [
    { address: 'comments.all_comments' }
  ]
)

server.listen(port_number, 'localhost')

# Starting value for the comments.
@comments = [{author: 'Sam Sneed', text: 'An example comment' }]

# We'll handle the browser requests here in these two handlers 

Vertx::EventBus.register_handler('comments.get_comments') do |message|
  message.reply({comments: @comments})
end

Vertx::EventBus.register_handler('comments.create_comment') do |message|
  @comments.push(message.body)
  Vertx::EventBus.publish('comments.all_comments', {comments: @comments})
end

Most of the ReactJS front-end code can be left intact, just the AJAX calls can be replaced with websocket wonderfulness.

And the following code will help simplify the React components:

var eb;
var initializeEventbus = function(component) {
    eb = new vertx.EventBus(window.location.protocol + '//' +
                    window.location.hostname + ':' +
                    window.location.port + '/eventbus');

    eb.onopen = function() {
          component.fetchComments();

          eb.registerHandler('comments.all_comments', function(event) {
            component.renderComments(event.comments);
          });
       };
}

This binds a single component to the eventbus, which is a consequence of the .onopen method working the way it does, but it will take a bit more digging to fix that.

So the fetchComments method looks like this:

fetchComments: function () {
    var component = this;

    eb.send('comments.get_comments', {}, function(result) {
        component.renderComments(result.comments);
    });
},

Much simpler than the AJAX call.

And handleCommentSubmit can simplify to:

handleCommentSubmit: function(comment) {
    var comments = this.state.data;
    var newComments = comments.concat([comment]);

    this.setState({data: newComments}, function() {
      eb.send('comments.create_comment', comment);  
    });
},

Which, again, is seems much simpler. I am pretty happy with how this is shaping up, and it seems that the "proper" solution will involve a workflow much more like React Component -> Flux Dispatcher -> Vert.x bus. That should mitigate the eventbus->single component binding I was chumped out by in this example and yes. It just works. Thank you Facebook!

The full code is at:

https://github.com/litch/vertx-reactjs-tutorial/blob/master/src/main/resources/web/index.html

Categories: Software