Have you spent hours or days searching for a simple Rails Action Cable to React tutorial? Well, look no further!
This tutorial is for developers with a basic understanding of Rails and React. We’ll be using Rails, React and Webpack to create a real-time chat app. I’ll also briefly cover deploying to Heroku with Redis as well. Live demo
Section 1. Rails and Action Cable
Let’s first create a Rails app.
rails new actioncable_app rails db:create
Then, we’ll create a Model, Controller and Channel for our messages.
rails g model Message body:string rails g controller Messages rails g channel Chat rails db:migrate
Next, go to your chat.coffee file and comment out everything here. What I discovered the hard way, is that when using React with Action Cable, you don’t need any of Rails default CoffeeScript. We can use Action Cable with our own JS code.
Now to set up Action Cable, go to chat_channel.rb.
In this file, we are going to create a stream within our ChatChannel. In this stream, clients and server will send and receive data. This stream is the WebSocket line of communication between clients and server. This is how we transmit data without the traditional HTML or AJAX request and response.
After we create a stream, we’ll define a method called speak. This method will receive data, manipulate that data to create a new instance of a Message, and then broadcast that new message into the stream. See below.
Notice that I created a hash variable called socket instead of just broadcasting the message body directly. This is because Action Cable only allows you to broadcast objects. Broadcasting a string will throw an error.
Next, create a root html file to render our React components. Inside app/views/messages. Create a root.html.erb file and add this one line of code.
Go to routes.rb and add a couple of lines to your code as shown below. Notice we don’t need any HTML routes since we are using WebSocket instead.
Let’s pause at this point. Start your Rails server and check that your local host page renders the root.html.erb file.
Next, we’ll configure our Webpack and create React components.
Section 2. React and Webpack
We’re going to use NPM to install React and Webpack. If you don’t already have NPM, install it here. Then run the following lines of code.
npm init npm install webpack webpack-cli react react-dom @babel/core @babel/preset-react @babel/preset-env babel-loader
Webpack bundles our JS files, gives us helpful errors and Babel transpiles JSX to JS and translates JS to <ES6. There are a few ways to bundle JS files, but Webpack gets the job done.
To stay organized, create a frontend folder in the root of your Rails app. We’ll keep all of our React files here.
|- actioncable_app |- frontend |- ChatRoom.js |- index.js |- MessageForm.js
We’ll create our index.js and render some text to start.
Now before we get too deep into React, let’s configure our Webpack and make sure we render our index.js component. Create a webpack.config.js file in your root directory and include the following code.
Now let’s bundle our JS files with Webpack.
webpack --mode=development --watch
Your local host page should now render the index.js component. In addition, Webpack will watch changes you make to your JS files by updating the bundle.js file, or it’ll give you helpful error messages. Neat!
Note, you probably want to git ignore your bundle.js.
Section 3. React and Action Cable
Now to the good stuff. We’ll start with our main ChatRoom.js component.
In our componentDidMount function, we call a new function from App.cable.subscription. Recall the chat_channel.rb file from Rails. This snippet here is essentially the client-side counterpart.
The App.cable.subscription.create function takes in at least 3 important arguments.
- Channel: We create a subscription to the ChatChannel channel. Note that the client only has to invoke this function and create a subscription once. Until the client unsubscribes, this subscription to the ChatChannel will persist.
- Received: When the client is subscribed, they will be listening to the Channel’s stream for any new data. When data is transmitted into the stream via the broadcast method called in the back end, this received function will be invoked.
- Speak: This function sends data to the back end. Recall in our chat_channel.rb file, we also have a method named speak. While these are two distinct functions, calling speak on the frontend, in turn invokes speak on the back end through the App.cable’ s perform function.
Let’s take a moment and recap a couple of things.
Where did App.cable come from?
Check out cable.js that was created by Rails. You’ll see this code below. This is where App.cable was defined.
Local state or Redux store?
If you’re creating a full-stack app you’ll probably want to use the Redux store. For example, when data is received on the client-side, instead of setting local state, you could dispatch a Redux action to update the store. In addition, you may even want to separate out the App.cable.subscription.create function call into its own component.
Almost done. Now we’ll create a MessageForm.js component to handle the submission of new messages.
Notice in our handleSubmit function, we are invoking the speak function that we defined in the ChatRoom.js file. We key into our subscription, located at index  because we only have one, and then invoke speak. Why Action Cable creators made it so App.cable.subscriptions.subscriptions is the way to access all subscriptions, I don’t know. It looks janky but it works. Note that once again, we pass data through Action Cable as an object.
Lastly, let’s update our index.js component and add some template CSS.
Template CSS below:
Congrats! You’ve created an app with a WebSocket connection through Rails Action Cable and React!
Section 4. Advanced Action Cable features(optional)
We’re going to create another subscription function called load, that will query all of the messages and send that data through the WebSocket. We’ll also update our received function to parse the data type being transmitted from the back end.
Update chat_channel.rb to add new load method and socket types.
Update ChatRoom.js. Add new functions.
Section 5. Heroku and Redis
To keep this within scope, I’ll assume that you’ve successfully deployed your app to Heroku, but the WebSocket just won’t work. Took me a few days on the first try. No sweat, just a few easy steps.
On your overview page, include the Heroku Redis add-on. Heroku requires that you add credit card information for account verification, but you won’t have to pay anything.
Next go to your gem file and uncomment out the Redis Gem. Bundle install.
Lastly, go to cable.yml file. Update the production section url to look like this.
Your Heroku app should now work with WebSocket features. That’s all folks! Thank you for reading and I hope this helped! Special shout out to Editor-in-Chief Andrew Gregory.