Action Cable with Redux!

M Beaudoin
6 min readJul 18, 2021

My first idea for building up my React and redux skills was to build a little messaging application. The way React can handle dynamic and compartmentalized rendering with components seemed to really suggest it’d be a no biggy at first blush, and with redux controlling a single source of truth with its store I was sure I would finish ahead of the pack with time to jump in an alpine lake before summer ended.

Ahhhh hubris.

What I hadn’t really thought about was the fundamental way in which messaging applications differ from most types of applications. Most web applications, and a lot of software in general really, is designed with a single user interacting with that application. The application really only needs to listen to one person at a time, and can generally just wait around until that person requests more things from it.

Chat applications need to manage input from multiple people at the same time, and, more importantly, need that information to be shared with other users before those other users actively refreshed their browser. It makes simple sense- how fun would a chat application be if you had to hit refresh every moment you thought there might be a message waiting?

In Rails, the Action Cable library allows an application to switch to a websocket protocol to create an active, 2 way connection between devices. This way, that messages are automatically pushed to the people involved. I found a fair amount of resources online supplying a lot of information on how to implement all this on vanilla React application, but resources on how to get this all going with Redux were very slim. I’ll walk you through the tricky parts, and explain what is happening along the way.

So, to recap, we want to create an Redux application with a Rails backend API that updates as content is added. Websockets allow us to upgrade the connection protocol to allow bidirectional transfers. Action Cable will (relatively) seamlessly integrate websockets into our rails API and our React application. I’ll briefly touch on the uniqueness of the backend, because it seems to be pretty well handled on the internet already.

One thing that will look different for those unfamiliar with websockets, is our Channel classes.

Action Cable allows users to subscribe to things, in this case, a user can subscribe to specific conversations. This is what allows the server to broadcast the data to the people who want it.

We’ll also define a websocket route in config:

If you haven’t yet, I highly recommend checking out the rails documentation for Action Cable. The rails team makes extremely useful guides and this one is no different.

Getting the websocket connection up is pretty simple for the front end:

Our index.js
And our constants

The main thing to note here is that we’re setting up ActionCableProvider with the ws: address we just defined on the backend, and that we’re also setting up our redux store.

In our Redux application, we want everything to sort of flow through the redux store. Everything should get everything they need from the store, and all our interactions with the server should be controlled by Redux.

In most chat applications, you’ll want a list of chatrooms or conversations to select from. To get that information from the server, we want to fetch through a redux action. We’ll have to use thunk for this, as we’re giving a function instead of a plain object as the return. Let’s walk through:

So here we have that list of conversations. As a react component, we’ll use componentDidMount to fetch our data when it first loads. Without redux, we’d just put our fetch in here and call it done, but we are going to call a function, and we’ll call setState, which will load our conversations when they do fetch. This fetchConversations function is an redux action we’ve imported, and we’ll also export it with connect.

the fetch is just your everyday normal fetch.

This takes care of the initial setup of our store, and our list of conversations. Next, we’ll dig into the fun parts.

We need to set up a subscriptions with our backend for each conversation, and create a way for people to add conversations:

Here ActionCableConsumer is creating a connection with the conversations channel on our back end, which will update when new conversations are added. We’re passing a function to the onReceived prop, which is a specific property from Action Cable. Basically, when this component receives data, pushed from our server, it will use this function to figure out what to do with it.

You will also notice the Cable component. This is just a normal react component, but one that we will use to create ActionCableConsumers for every conversation, which is why we’ll pass all our conversations from state, and a similar function to handle them. The onReceived functions are:

We simply taking the response and setting it as an object, which we can then pass to our actions. addMessageToRedux and addConversationToRedux are both imported from our redux actions, and are pretty simple. What is very important to realize, though, is that we are now deviating from our normal redux flow!

In normal Redux, when you post to a server, the server responds with a confirmation or an error. Generally the backend controller looks like this

Our controllers won’t be rendering json. Instead they will use Action Cable to broadcast the data. Basically, instead of controllers giving you (and only you)the data you hopefully saved successfully, they’ll be broadcasting the data to everyone who subscriber to it.

What this means, effectively is that our post fetches just kinda of end, they don’t receive information back. If you have a promise, it’ll be broken every time.

Here’s what our actions look like instead:

The first one is for adding a new conversation to the server. This is from form submissions, like normal. Notice we don’t have a promise! No “.then”. It just finishes posting and resolves. Instead of updating the store with promises, we hook into the onReceived property, which is receiving the information we hopefully just posted. That calls the addConversationToRedux action, and this returns the action object that the reducer needs to update the store.

We basically just broke the process down into 2 parts, the actual posting and the updating based on successful posting. The beauty is that this allows EVERYONE to update, even when they didn’t send a post fetch.

Now if we have all our normal redux flow in order, our components will automatically update based on the state changes, and everything will just cascade down seamlessly!

The main concept to really grasp is how this process differs from traditional react/redux applications, and what we need to do to allow that to function properly. Once you start realizing what is happening behind the curtain, it all falls into place. Hope this helps! Happy hacking!

--

--