---
title: "Using ReactJS with Rails Action Cable"
description: "Using ReactJS with Rails Action Cable for real-time applications."
canonical_url: "https://www.bigbinary.com/blog/using-reactjs-with-rails-actioncable"
markdown_url: "https://www.bigbinary.com/blog/using-reactjs-with-rails-actioncable.md"
---

# Using ReactJS with Rails Action Cable

Using ReactJS with Rails Action Cable for real-time applications.

- Author: Vipul
- Published: July 19, 2015
- Categories: Rails

Recent DHH, announced availability of alpha version of Action Cable.

<blockquote class="twitter-tweet" data-cards="hidden" lang="en">
  <p lang="en" dir="ltr">
    Action Cable v0.1.0 is now available for alpha testers:{" "}
    <a href="https://t.co/ryqK2ZAKG7">https://t.co/ryqK2ZAKG7</a>. I&#39;ll be
    setting up a demo repo shortly as well. ENJOY!
  </p>
  &mdash; DHH (@dhh){" "}
  <a href="https://twitter.com/dhh/status/618886698009759748">July 8, 2015</a>
</blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

[Action Cable](https://github.com/rails/actioncable) is still under heavy
development but it comes with
[examples](https://github.com/rails/actioncable-examples) to demonstrate its
usage.

Action Cable integrates websocket based real-time communication in Ruby on Rails
applications. It allows building realtime applications like Chats, Status
updates, etc.

## Action Cable + React

Action Cable provides real time communication. ReactJS is a good tool to manage
view complexity on the client side. Together they make it easy to develop snappy
web applications which requires state management on the client side without too
much work.

Anytime data changes the new data is instantly provided by Action Cable and the
new data is shown on the view without user doing anything on the application by
ReactJS.

## Integrating React

The official Action Cable Example is a chat application. We will be building the
same application using [ReactJS](https://facebook.github.io/react).

First follow the
[instructions mentioned](https://github.com/rails/actioncable-examples) to get a
working chat application using Action Cable.

Now that the chat application is working let's get started with adding ReactJS
to the application.

_Please note that we have also posted a number of
[videos](https://bigbinary.com/videos) on learning ReactJS. Check them out if
you are interested._

## Step 1 - Add required gems to Gemfile

```ruby

# react-rails isn't compatible yet with latest Sprockets.
# https://github.com/reactjs/react-rails/pull/322
gem 'react-rails', github: 'vipulnsward/react-rails', branch: 'sprockets-3-compat'

# Add support to use es6 based on top of babel, instead of using coffeescript
gem 'sprockets-es6'
```

## Step 2 - Add required JavaScript files

Follow
[react-rails installation](https://github.com/reactjs/react-rails#installation)
and run `rails g react:install`.

This will

- create a components.js file.
- create `app/assets/javascripts/components/` directory.

Now put following lines in your application.js:

```javascript
//= require react
//= require react_ujs
//= require components
```

Make sure your `app/assets/javascripts/application.js` looks like this

```javascript
//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require react
//= require react_ujs
//= require components
//= require cable

//= require channels
//= require_tree .
```

## Step 3 - Setup Action Cable to start listening to events

We will be using es6, so lets replace the file
`app/assets/javascripts/channels/index.coffee`, with
`app/assets/javascripts/channels/index.es6` and add following code.

```javascript
var App = {};
App.cable = Cable.createConsumer("ws://localhost:28080");
```

Also remove file `app/assets/javascripts/channels/comments.coffee`, which is
used to setup subscription. We will be doing this setup from our React
Component.

## Step 4 - Create CommentList React Component

Add following code to `app/assets/javascripts/components/comments_list.js.jsx`.

```javascript
var CommentList = React.createClass({
  getInitialState() {
    let message = JSON.parse(this.props.message);
    return { message: message };
  },

  render() {
    let comments = this.state.message.comments.map(comment => {
      return this.renderComment(comment);
    });

    return <div>{comments}</div>;
  },

  renderComment(comment) {
    return (
      <article key={comment.id}>
        <h3>Comment by {comment.user.name}</h3>
        <p>{comment.content}</p>
      </article>
    );
  },
});
```

Here we have defined a simple component to display a list of comments associated
with a message. Message and associated comments are passed as props.

## Step 5 - Create message JSON builder

Next we need to setup data needed to be passed to the component.

Add following code to `app/views/messages/_message.json.jbuilder`.

```ruby
json.(message, :created_at, :updated_at, :title, :content, :id)
json.comments(message.comments) do |comment|
  json.extract! comment, :id, :content
  json.user do
    json.extract! comment.user, :id, :name
  end
end
```

This would push JSON data to our `CommentList` component.

### Step 6 - Create Rails Views to display component

We now need to setup our views for Message and display of Comments.

We need form to create new Comments on messages. This already exists in
`app/views/comments/_new.html.erb` and we will use it as is.

```ruby
<%= form_for [ message, Comment.new ], remote: true do |form| %>
  <%= form.text_area :content, size: '100x20' %><br>
  <%= form.submit 'Post comment' %>
<% end %>
```

After creating comment we need to replace current form with new form, following
view takes care of that.

From the file `app/views/comments/create.js.erb` _delete_ the line containing
following code. Please note that below line needs to be deleted.

```ruby
$('#comments').append('<%=j render @comment %>');
```

We need to display the message details and render our component to display
comments. Insert following code in `app/views/messages/show.html.erb` just
before `<%= render 'comments/comments', message: @message %>`

```erb
<%= react_component 'CommentList', message: render(partial: 'messages/message.json', locals: {message: @message}) %>
```

After inserting the code would look like this.

```ruby
<h1><%= @message.title %></h1>
<p><%= @message.content %></p>
<%= react_component 'CommentList', message: render(partial: 'messages/message.json', locals: {message: @message}) %>
<%= render 'comments/new', message: @message %>
```

Notice how we are rendering CommentList, based on Message json content from
jbuilder view we created.

## Step 7 - Setup Subscription to listen to Action Cable from React Component

To listen to new updates to comments, we need to setup subscription from Action
Cable.

Add following code to `CommentList` component.

```javascript
setupSubscription(){

  App.comments = App.cable.subscriptions.create("CommentsChannel", {
    message_id: this.state.message.id,

    connected: function () {
      setTimeout(() => this.perform('follow',
                                    { message_id: this.message_id}), 1000 );
    },

    received: function (data) {
      this.updateCommentList(data.comment);
    },

    updateCommentList: this.updateCommentList

    });
}
```

We need to also setup related AC Channel code on Rails end.

Make following code exists in `app/channels/comments_channel.rb`

```ruby
class CommentsChannel < ApplicationCable::Channel
  def follow(data)
    stop_all_streams
    stream_from "messages:#{data['message_id'].to_i}:comments"
  end

  def unfollow
    stop_all_streams
  end
end
```

In our React Component, we use `App.cable.subscriptions.create` to create a new
subscription for updates, and pass the channel we want to listen to. It accepts
following methods for callback hooks.

- `connected`: Subscription was connected successfully. Here we use `perform`
  method to call related action, and pass data to the method.
  `perform('follow', {message_id: this.message_id}), 1000)`, calls
  `CommentsChannel#follow(data)`.

- `received`: We received new data notification from Rails. Here we take action
  to update our Component. We have passed
  `updateCommentList: this.updateCommentList`, which is a Component method that
  is called with data received from Rails.

### Complete React Component

Here's how our complete Component looks like.

```javascript
var CommentList = React.createClass({
  getInitialState() {
    let message = JSON.parse(this.props.message);
    return { message: message };
  },

  render() {
    let comments = this.state.message.comments.map(comment => {
      return this.renderComment(comment);
    });

    return <div>{comments}</div>;
  },

  renderComment(comment) {
    return (
      <article key={comment.id}>
        <h3>Comment by {comment.user.name} </h3>
        <p>{comment.content}</p>
      </article>
    );
  },

  componentDidMount() {
    this.setupSubscription();
  },

  updateCommentList(comment) {
    let message = JSON.parse(comment);
    this.setState({ message: message });
  },

  setupSubscription() {
    App.comments = App.cable.subscriptions.create("CommentsChannel", {
      message_id: this.state.message.id,

      connected: function () {
        // Timeout here is needed to make sure Subscription
        // is setup properly, before we do any actions.
        setTimeout(
          () => this.perform("follow", { message_id: this.message_id }),
          1000
        );
      },

      received: function (data) {
        this.updateCommentList(data.comment);
      },

      updateCommentList: this.updateCommentList,
    });
  },
});
```

## Step 7 - Broadcast message when a new comment is created.

Our final piece is to broadcast new updates to message to the listeners, that
have subscribed to the channel.

Add following code to `app/jobs/message_relay_job.rb`

```ruby
class MessageRelayJob < ApplicationJob
  def perform(message)
    comment =  MessagesController.render(partial: 'messages/message',
                                         locals: {message: message})
    ActionCable.server.broadcast "messages:#{message.id}:comments",
                                 comment: comment
  end
end

```

which is then called from `Comment` model, like so-

Add this line to `Comment` model file `app/model/comment.rb`

```ruby
after_commit { MessageRelayJob.perform_later(self.message) }
```

We are using message relay here, and will be getting rid of existing comment
relay file - `app/jobs/comment_relay_job.rb`. We will also remove reference to
CommentRelayJob from `Comment` model, since after_commit it now calls the
`MessageRelayJob`.

## Summary

Hopefully we have shown that Action Cable is going to be a good friend of
ReactJS in future. Only time will tell.

Complete working example for Action Cable + ReactJS can be found
[here](https://github.com/vipulnsward/actioncable-examples/tree/es6).

## Links

- [Human page](https://www.bigbinary.com/blog/using-reactjs-with-rails-actioncable)
