Ready, Set, Action (Cable)

A post detailing how I utilized Rail’s Action Cable with a React Front-End in the simplest way I can explain it.

Michael Sutton
Jun 12 · 8 min read
Photo by Steve Harvey on Unsplash

For my final project at Flat Iron, I needed real-time rendering vs. your typical Post/Get HTTP protocol. An example of real-time rendering would be the chat window for tech support for your cable company. Both you and the tech support are seeing messages in real-time. The chat window is actually utilizing an alternative Protocol called web-sockets. I needed my data to be the same for end-users on the same URL, even on a separate computer.

WebSocket is a computer communications protocol, providing full-duplex communication channels over a single TCP connection.

As you see in the example below. The window on the left submits a new room, and it automatically appears in the other browser (right).

This is the magic of Action Cable at work. It’s beautiful when it works, but I had my fair share of troubles and struggles getting to this point. So this article will entail how you can set up action cable on the front-end and back-end to function. I’ll try to do this in the simplest way possible so you can create your own functioning action cable without all the hours I had to spend debugging and trying to get it to work after reading countless articles. Some of the articles I did read and found helpful are these: https://dev.to/christiankastner/react-and-actioncable-1gbh & https://medium.com/@dakota.lillie/using-action-cable-with-react-c37df065f296

Also, it should be mentioned that there may be alternative albeit better integrations for this technology, but this is what worked for me.

Let’s get this party started.

First things first, you’ll need to do is set up a project folder in the Terminal type:

Then set up your Rails API :

This will create the Rails API. The --api flag will create an API versus a full-blown Rails Application. -T is a flag to skip the rails test framework.

Afterward, in the Terminal cdinto the Rails folder. To get our backend to accept requests from the front-end, we need to uncomment gem 'rack-cors' and uncomment gem ‘redis' while you’re in the Gemfile. Action Cable utilizes Redis. Run bundle install with the command bundle Then open config/initializer/cors.rb in your text editor, uncomment the Rails.application.config…. block, and set the orgins and resource to '*' This sets the Cors (Cross-Origin Resource Sharing) to accept any origin.

Note this configuration is just for development purposes do your research on how to set up Cors for production.

Next, we need to generate our resources:

For this article, I will be showing the power of ActionCable by having a simple list of Rooms. Feel free to make your models as complex as you need them to be.

This will generate:

Now open your config/routes.rb file and modify it to be the same as below:

I have limited my routes for my room model, but feel free to add or delete as necessary. The only needed ones for our purpose are :index, :showand :create Also, please note all the routes file needs to instantiate the WebSockets connection is the line on line 5 above. As mentioned earlier, ActionCable is a full-duplex connection, so there’s no setting post or get routes.

If you’ve utilized Rails as a backend before, all of this should be pretty familiar. However, this is where we begin to embark on a journey into some new territory.

When you auto-generate your rails application, it actually creates a folder called app/channels 😵 I didn’t realize this. The channels can be thought of as the controller's folder for WebSockets. While I have seen the design pattern to build out all the WebSocket functionality within the channel, we will rely on what I know, Controllers.

Make sure you're still in the root folder of your Rails API:

This will create a new file in app/channels/rooms_channel.rb

What does this file do? Well by mounting ActionCable.server it opens the cable of communication of WebSockets. Actual connections need to be initiated by subscribing to a channel. The subscribing will be handled on the front end. But we do need to modify this file.

All we need to do is add stream_from "rooms_channel" to Rooms_Controller.rb

It’s worth mentioning here that you can actually set up your Channel to handle parameters, e.g., a room id. Similar to how you use params for :show in your controller

When we ran rails g resources Room name It made a bunch of relevant files, so our Controller already exists app/controllers/rooms_controller.rb

We will need to add our controller actions for Index, Create, and Show.

The majority of this controller is relatively straightforward, except for ActionCable.server.broadcast 'rooms_channel', room It is paramount that 'rooms_channel' matches what you typed out in the Channel file.

What this line is saying is, “Hey, ActionCable broadcast out to all the subscribers of rooms_channelmy data in room

So that’s it for what’s needed for the backend. Well, except for creating our Database. In the terminal type:

Then verify everything by starting the API server

Then open http://localhost:3000/rooms to confirm its functioning. It should just display an empty Array at this point.

React and ActionCable

Now it’s time to put the front-end together to connect and display our WebSocket content. If you still have your terminal window within the Rails folder, back out of it into the root directory of your project folder.

Then type into the terminal.

This will create a new React application. Next, we will need to install the ActionCable plugin for React. If you take a look at other articles, they will recommend various plugins like react-actioncable-provider /@rails/actioncable or actioncable all are available from npm. The one that worked for me is actioncable. So this will be the one we will install.

Make sure you’re still in your react app directory and type into the terminal

This will install the plugin necessary to communicate with the backend. The -S flag saves it as a dependency of the project. Next, let’s set some global constants up by creating a folder and an index.js. In the terminal type, after confirming you're still in the root directory of the React app.

Now open the file you just created and add:

This might be overkill for the simplistic example we are building, but this is the way I like to work. And it’s a good pattern to avoid having to change constants all over the place.

Now make sure you're still in /src in the terminal and type

This is where we will connect the WebSockets to the backend. Now open the Cable.js in a text editor and add:

This is creating a Consumer that is necessary to then subscribe to a channel—more in regards to this file in a moment.

Next, let’s set up our App.js file, so open it in a text editor and modify it as below.

Also, you need to install react-router-dom for the URL routes to function

All I’m doing here is creating two URL routes to either load the index of rooms or an individual room. Next, add a little bit of CSS App.css So the display of the room names is slightly prettier. 😃

Okay, now it’s time to build out those components that are referenced in App.js Hopefully, your terminal is still in the src/ folder. If so, type:

I swear we are getting close. We need to build out a few components, and we are about done.

Up first is the NewRoomForm.js this will be how you’ll submit a new room. Since this is just your standard React form, go ahead and copy and paste this into the file.

Room.js is the display component for the room objects. So it’s pretty straightforward. Just go ahead and copy and paste this code into it.

Next up is ShowRoom.js while not totally necessary, I wanted to be able to click through to display the individual rooms. Just copy and paste this code.

Lastly is RoomList.js It’s a long one! And where all the magic happens, so look through the source code, and you’ll find relevant comments.

Okay, let’s dive into what’s going on here so you have a better understanding. Line 8 import cable from '../../services/Cable’ is importing the function to create a Consumer.

The Flow Goes: ActionCable → Consumer → Subscription

Then cable is called by roomsChannel() which creates a subscription

The rest of the function contains callback functions for events in regards to the subscription. Since this is just a development phase, I left the console.log() to track events. The only callback that matters isreceived: data => {} this is the quintessence of WebSockets and how all this ultimately works to update the DOM across all user’s browsers. Although I will admit maybe this isn’t the most streamlined code structure, making an additional Fetch() call when data is received makes the most sense. Especially when you're trying to avoid duplicate items being added to the DOM, and you don’t have to be concerned if this particular user is the one who submitted the new Room.

User submits a New Room → POST Fetch() → The Server then broadcasts out that data to all the subscribers → This event triggers loadRooms() for all users connected to this URL as subscribers.

Now Let’s See It In Action

You’ll need to fire up the rails server first, so it uses the default port of 3000. Then, in the terminal, navigate to the Rails API and enter the command.

Next, open a new tab or window in the terminal, navigate to the React front-end, and enter the command.

It’ll yell at you for not being able to use Port 3000, but go ahead and say yes to use another port. If all has been typed correctly, You should be staring at our super basic application. Go ahead and add a room. Now open another tab and navigate to the same URL. On the new tab, add another room. Navigate or look at the other open tab

This took entirely way longer than I expected it to be. If you stuck it out, you have reaped the rewards of seeing the magic of Action Cable. Your welcome! I felt after my difficulty trying to implement it into my project. I needed to show how I did it in hopes others won’t fall into the same trap as I did. Happy Coding.

Geek Culture

Proud to geek out. Follow to join our 1M monthly readers.