Action Cable Rails ChatApp with React hooks
If you came across this article, you either have heard of Action Cable before or you are looking to dive in to learn about Action Cable and how you can use it in your project. I was on the same boat and went into this rabbit hole of trying to find the best resources and reading different blog posts & eventually learning how this can work in my app. This blog post is going to be a long one, because i would like to try to cover as much as possible.
As every project is different and you may have different ideas or things in place but i will try to help you get started if this is your first time using Action Cable. This is how my Project looks like
Action Cable allows for real-time features to be written in Ruby in the same style and form as the rest of your Rails application, while still being performant and scalable. Action Cable uses WebSockets instead of the HTTP request-response protocol. Both Action Cable and WebSockets introduce some less familiar terminology. It’s Recommended to read into rails guide to get yourself familiar with the terminology as it will make sense with the rest of the blog post. https://guides.rubyonrails.org/action_cable_overview.html#terminology
What are WebSockets?
WebSocket is a two-way computer communication protocol over a single TCP. This is a common definition for it. Using WebSockets is a good way to handle high scale data transfers between server-clients. We will use webSockets instead of http for our app as it supports the full-duplex Communication faster.
more on webSockets https://en.wikipedia.org/wiki/WebSocket
Let’s start with the relationship among my models and how i structured it before starting the project
Controllers used are :- MessagesController, UsersController ChatroomController, SessionsController (for authentication/authorization)
Now, to create ActionCable Connection i first had to create a route in my routes.rb
and in Cable.yml file change the adapter to redis and uncomment the redis gem from gemfile and then bundle install to install redis gem
Action Cable uses Redis to send and receive messages over the channel. So, when we told our Action Cable server to #broadcast to ‘messages’ , we were actually saying “send new messages to the ‘messages’ channel maintained by Redis.”
next, in our channels/application_cable we have connection.rb, where we are going to set up the WebSocket connection.
For every WebSocket accepted by the server, a connection object is instantiated and it’s sole job is to authenticate and authorize the current user.
Next we are going to create the Chatrooms Channel which will do the job of broadcasting our messages to all the users in that channel because they are technically subscribed to the same channel when that websocket connection is established.
this is how my ChatroomsChannel looks like
Okay, i know this can look confusing but what we are trying to do here is in our subscribed method we are going to find the chatroom and then we are streaming for that chatroom which means after connection is established our frontend will create the connection and send that to backend and then our backend will stream on that channel live
in our received method we are receiving data from our frontend to broadcast and using ChatroomsChannel.broadcast_to we are broadcasting the information we sent from frontend to basically all the users .
how would that work in our messages controller ?
in our messages controller i have create method that when that message gets created it broadcasts to ChatroomsChannel.
now before we proceed to set up the connection with react we need to make sure that data we receive from the backend is how we are going to structure it in our frontend as well.
i used the fastJsonapi for serializer, it converts json data to serializable hash
now let’s move to frontend to create the cable connection
in our index.js import actionCable from ‘actioncable’ , you will have to install the actioncable package do- npm i actioncable on your client side
cableApp.cable=actionCable.createConsumer(‘ws://localhost:3000/cable’). This will create the new consumer (every subscription to a channel needs consumers to be subscribed to it )and notice we didn’t use http here, instead we are using webSocket to have that faster full duplex connection.
Now we have established the 1st step to create a consumer we are going to pass down this cableApp to our main App component as it will be used later to send the data to backend.
we will create a component that will Subscribe the consumer to the Chatrooms Channel and the room here will have the id, if you remember below we tried to find the chatroom by room params this is where that room is coming from
The updateApp and getRoomData methods are the methods that are getting the room information and one is updating on the client state for the current rooms data but also with that sending the information to the backend
These are the methods i have in my App.js and they are being passed down to the component <RoomShow> which shows an individual chatroom which then have the <RoomWebSocket> component but it’s only sole purpose is to create the subscription so it just exists in the <RoomShow> like this
and that’s it, now you can submit your messages and they will be broadcasted to every person in the channel and you can have real time communication.
Bonus: you can add update and destroy options to your project like i did
Once i updated it, it broadcasted the changes to all the users that are subscribed to the channel. i could also delete a message and that also got updated in real time.
to make this achievable i made some changes to my messages controller as follow. i created a private method broadcast that takes current chatroom as an argument and broadcasts for that channel and called that method inside my update, destroy methods as well for the changes to be broadcasted.
and that’s it. now you have fully functioning Action cable chat app with all the necessary chat features.
There were very limited resources on Action Cable using React Hooks when i was trying to learn Action Cable, it can sure seem as a challenge when you first come across it but once you see how great it can be, i would say that challenge is worth it!