How to Use Action Cable With React and Rails

Beginner’s tutorial on how to utilize WebSockets via Action Cable

Jackson Chen
The Startup
4 min readSep 28, 2020

--

Data
Photo by fabio on Unsplash

Have you ever wondered how some websites are able to stream real-time sports data or how users are able to receive messages almost as instantly as soon as they were sent?

WebSockets

In this tutorial, we’ll go over how to utilize WebSockets via Action Cable in a React frontend and Rails backend simple chat web application for real-time communication between multiple clients.

1. Initial Setup

Rails Backend

After initializing your new rails application, enable the redis gem in your Gemfile and add the following route to your routes.rb file.

# rails-backend/config/routes.rb mount ActionCable.server => '/cable'

Remember to run $ bundle install after enabling redis.

React Frontend

Next we will need to install Action Cable on our React frontend.

$ npm install @rails/actioncable

Create a separate JavaScript file that will be responsible for handling and creating the initial WebSocket connection to your Rails backend.

$ touch ./src/cable.js

Import createConsumer from the Action Cable library. Assuming your Rails server is running on port 3000, fill in the following.

// react-frontend/src/cable.jsimport { createConsumer } from '@rails/actioncable';const URL = 'ws://localhost:3000/cable';
const consumer = createConsumer(URL);

export default consumer;

Notice for the URL, we’re not using http anymore; instead, we’re using ws which stands for WebSocket. This creates an open connection between the consumer and the server that will allow them to receive any message(s) broadcasted as long as they are subscribed or connected.

Import consumer to the React component where you want the connection to be present. In this example, we’ll be displaying our messages to a React component called ChatBox.

// react-frontend/src/ChatBox.jsimport React from 'react';
import consumer from './src/cable';
export default class ChatBox extends React.Component { . . .};

2. Initialize Channels

Rails Backend

In our Rails backend server, we will need to initialize channels for our consumers/subscribers to connect to. In your terminal, use the following command to generate a channel. In this example, we’ll be creating a channel called chat.

$ rails generate channel chat

This will generate the following in your Rails directory.

# rails-backend/app/channels/application_cable/chat_channel.rbclass ChatChannel < ApplicationCable::Channel  def subscribed
end
def unsubscribed
end
end

3. Create Subscriptions

React Frontend

Now that the channels have been created, our clients or subscribers will need a way to connect to that channel. In the previous React Component where consumer was imported, add the following for creating subscriptions.

// react-frontend/src/ChatBox.jsexport default class ChatBox extends React.Component {  componentDidMount() {
consumer.subscriptions.create({
channel: 'ChatChannel',
username: 'cool_kid_20',
})
};
componentWillUnmount() {
consumer.disconnect()
};
. . .};

This will create a subscription or connection to our ChatChannel as soon as the ChatBox component renders. In this case, a username param will also be sent as well.

Upon unmounting of the component, the WebSocket connection will close. This is important to prevent memory leaks or hidden open connections within your web application.

4. Handling Subscriptions

Rails Backend

Once subscribed to a channel, we will have our subscribers actively listen for broadcasts sent to streams. In our example, the subscriber will begin listening for broadcasts sent to the 'public_chat’ upon subscribing and broadcast the string 'cool_kid_20 joined!' to every subscriber currently listening to the ‘public_chat` stream.

# rails-backend/app/application_cable/channels/chat_channel.rbdef subscribed
user = params['username']
stream_for 'public_chat'
ActionCable.server.broadcast 'public_chat', "#{user} joined!"
end

5. Handling Broadcasts

React Frontend

When any message is broadcasted at anytime and as long as we are subscribed and listening to the same stream, our subscriber will be able to receive that data. In this example, we will just console.log() the data that was broadcasted.

// react-frontend/src/ChatBox.jsComponentDidMount() {  consumer.subscriptions.create({
channel: 'ChatChannel',
username: 'cool_kid_20',
}, {
connected: () => console.log('connected'),
disconnected: () => console.log('disconnected'),
received: data => console.log(data),
})
};

6. Broadcasting Messages

React Frontend

Assuming we have a form that will send a POST request to our backend upon submit, we can send a message to be broadcasted to the ChatChannel public_chat stream.

// react-frontend/src/ChatBox.jsstate = {
content: 'Hi!',
username: 'cool_kid_20'
}
handleSubmit = () => {
fetch('http://localhost:3000/messages', {
method: 'POST',
body: JSON.stringify(this.state)
})
}

Rails Backend

In the controller that will be handling the POST request, we will have the message broadcasted to 'public_chat' as long as the created message passes validation.

# rails-backend/app/controllers/messages_controller.rbdef create  user = User.find_by(params['username'])
message = Message.create(
content: params['content'],
user_id: user.id
)
if message.valid?
ActionCable.server.broadcast 'public_chat', message.content
end
render json: messageend

In order to fully test this out, open up a second browser in incognito mode and start sending messages!

There are a multitude of uses that WebSockets offer besides messaging. Whether it’s streaming live sports data, multiplayer game data or real-time collaboration, the uses are endless. Hopefully this tutorial was a good starting point for learning how to integrate WebSockets into your React/Rails web application. I know you’ll make something awesome.

Resources

--

--

Jackson Chen
The Startup

Pre-med turned software engineer. Writing about my experiences and what I’ve learned. Let’s connect. https://jacksonchen.dev