How to Use Action Cable With React and Rails
Beginner’s tutorial on how to utilize WebSockets via Action Cable
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?
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
endend
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.