Real time data flow with Redux and Socket.io

Phil Parsons

Tagged: , , ,

After the last article the development workflow is setup ready for us to start iterating on the code needed to build the application features. In this article we are going to connect two of the chat clients we have built so far using a web socket connection with Socket.io.

This post is part of the Developing for a modern web with React.js series. If you’d like to be notified of new posts in this series you can become a free member. If you don’t want to complete the previous articles before working through this tutorial you can download the code from Github.

Start by installing the Socket.io package with NPM.

The socket.io package allows us to create a socket server that will handle connections from clients and direct chat messages between them. A socket server allows for real time bidirectional messaging between a client and server through a dedicated connection so that clients do not need to poll the server for changes. You can read more about web sockets on the MDN developer network. Create a new folder under the server directory named socket-server and create the new file ./server/socket-server/index.js with the following code.

This socket-server module exports a single function that takes the Express application server as its only parameter. This simple implementation will serve us for the purpose of this article so that we can test the message passing between two browser tabs running the chat application. The socket server receives a message from one client and emits that same message to all other connected clients (we only want two to test out our chat for this article but it would work with more). Each socket connection is stored in an array called connections and when a message is received on a socket the list of connections is iterated over emitting the message to each one except for the socket that sent the message. When the socket disconnects it is removed from the connections array by splicing.

To initialise the socket server edit ./index.js to import the socket-server module and call the exported function passing it the Express application server.

Now when the Express application server is started the socket server is running and waiting for clients to connect. To create a connection add the following code to the client application in the new file ./client/chat.js.

In this module we include the client library that is bundled with Socket.io and create a connection to the server on the same host as our express application. To enable the communication between clients we need to be able to emit each added message to the server and listen for messages from other clients. First we’ll add the code to listen for a message and dispatch a new action to add the received message to the chat, let’s refer to these messages as responses. Edit ./client/chat.js and wrap the socket connection in an exported function that takes the store as it’s only argument.

The message action creators are imported and a the new action creator addResponse is used to create the action that is dispatched to the store when a message is recevied on the socket. Edit ./client/index.js to import the chat module and start the chat once the store is created.

Add the new addResponse action creator in ./client/actions/message-actions.js.

The new action needs to be handled in the reducer function so that the new message is added to the list of messages. Adding another case to the switch statement in the reducer function starts to raise a bit of a code smell. Look at how this might look.

There is code duplication and we can see that the addition of any more actions will face similar problems. We could move shared code to other helper functions but it’s at this stage that we need to make a better choice and refactor the code. Redux provides a utility called combineReducers that can be used to compose the separate parts of the state in the store using a combination of reducer functions. To achieve the level of separation wanted we need to move the logic that trims the message and checks that it is not empty to the MessageEntryBox component. First edit ./client/reducers/index.js to incorporate combineReducers.

There are now two separate functions for managing the individual sections of the state, currentMessage and messages, and it’s now much easier to understand how these parts of the state change with each action. Edit the MessageEntryBox component in ./client/components/message-entry-box/index.js to send the trimmed message on submit.

Lastly update the action creator to include the message.

Adding the Redux chat middleware

To send messages to the server we are going to create a middleware for Redux. Edit ./client/chat.js and make the following changes to export a middleware function called chatMiddleware.

This middleware function returns a function which applies the next action and if the action is of the ADD_MESSAGE type plucks the last message off the list and sends it to the server. Edit ./client/index.js to Import the middleware and apply it to the store using the applyMiddleware util from Redux.

Now every action will pass through this middleware function when dispatched and the two way communication with messages is established. Build and run the application using the npm scripts added in the last article. Once running open the chat application in two separate browser tabs and start a conversation with yourself. You should see the messages coming into the chat from the client in the other browser tab as you add them.

It’s working but the chat window is looking like it needs some styling and we can’t yet distinguish which message came from who. Before styling the chat window components let’s get the shape of the data for a message sorted.

We need to be able to identify each user so that we can assign the users identifier to each message. This way we can check the user id on each message to determine which messages are responses. Modify the socket server code to emit a new start event on successful connection that assigns an id to the client. Until we have a database installed we will use a simple counter for the user id. Edit ./server/socket-server/index.js and add the start event with the userId counter.

On the client we need to handle the start event by creating a new action to set the user id in the store. Edit ./client/chat.js and add the code to handle the start event.

Define the setUserId action creator in ./client/actions/message-actions.js

And add the reducer function for managing the userId attribute in ./client/reducers/index.js.

With these changes we now have the userId saved in the store state so we need to add it to the initial state we have in our server code. Edit ./server/index.js and add an empty string for the initial userId attribute.

The userId needs to be added to each message but we don’t want to muddle the concerns in our reducer functions. What we will do is replace the id we currently add to each message and compose the whole message from the MessageEntryBox component. Edit ./client/components/app/index.js and map the new userId property to the component props.

The userId is now passed into both the MessageList and MessageEntryBox components. Update ./client/components/message-entry-box/index.js to compose the message object with the text and user id.

Now both the ADD_RESPONSE and ADD_MESSAGE actions can be handled in the same way. We do need to keep the distinction though as the messages are sent to the socket server when the ADD_MESSAGE action is dispatched. Edit./client/reducers/index.js and refactor the messages reducer function.

Composing the message object in the component will also allow us to simplify the chat middleware as the whole message is available in the action. Refactor the middleware in ./client/chat.js.

Last thing that needs to be done is to edit the MessageList component and add a class name to messages that are responses. Edit ./client/components/message-list/index.js and add the is-response class name to every message where the userId does not match the userId in the component props. The HTML structure is also updated so that the messages can be styled more easily.

Styling the components

Okay, it’s about time we addressed the chat window styling. Feel free to style it how you would like or alternatively use the styles for the components shown below.

First edit ./client/components/app/style.less

then create the style sheet ./client/message-entry-box/style.less not forgetting to import it at the top of ./client/message-entry-box/index.js.

Last of all, create ./client/components/message-list/style.less again not forgetting to import it in the component index.js file.

Restart the servers if not already running and make conversation again between two browser tabs. You should see response messages shown on the other side of the chat in a different colour to the main messages.

There is quite a lot to take in from this article especially the way the code is refactored as more functionality is added. Refactoring is a common practice during development and a skill that is developed over time. Ways to separate concerns and reduce duplication where identified and addressed making the code easier to read and ultimately more maintainable.

This post is part of the Developing for a modern web with React.js series. If you enjoyed the tutorial and would like to follow along to future posts please sign up to become a free member.

It's only fair to share...Tweet about this on TwitterShare on FacebookShare on Google+Buffer this pageShare on LinkedInPin on Pinterest

/ 9 Articles

Phil Parsons

13 Comments

  1. Thomas

    Hi Phil, thanks a lot for your articles !

    I tried to deploy your branch on heroku but it doesn’t work…

    I obtain this message (sound like problem with import) :

    2015-12-21T09:35:18.585745+00:00 app[web.1]: /app/index.js:1
    2015-12-21T09:35:18.585754+00:00 app[web.1]: (function (exports, require, module, __filename, __dirname) { import server from ‘./server’;
    2015-12-21T09:35:18.585755+00:00 app[web.1]: ^^^^^^
    2015-12-21T09:35:18.585755+00:00 app[web.1]: 2015-12-21T09:35:18.585755+00:00 app[web.1]:

    Any idea of how to deploy it on a distant server ?

    Thanks in advance !

    Reply

    • Phil Parsons

      I’ll cover deploying the server code in a later post but essentially you will need to deploy the Babel output.

      Reply

  2. Ryan Steckler

    These are AWESOME articles. By far the best way to quickly get ramped up on the React stack. Thanks so much for creating these posts.

    Reply

    • Phil Parsons

      Thanks Ryan, I hope to get more out soon!

      Reply

  3. Steven

    AWESOME!! Thanks!

    Reply

  4. jombie

    Great articles read from start to beginning and eagerly waiting for next articles.
    Thanks a lot.

    Reply

  5. jombie

    By the way any planned dates for future articles.

    Reply

    • Phil Parsons

      Yes! I will be adding future articles soon as I am done with the development for the new site.

      Reply

  6. Alexa

    HI there. Best articles Ive read about redux and socket. Will you plan to write about rethinkDB?

    Reply

  7. Ben

    Hi Phil,

    I hope you haven’t given up on these because they are the best articles on the web for learning React, and that’s saying something there are a lot!

    I’d love to see you take on persistence and also deployment to a production server,

    cheers,

    Ben

    Reply

    • Phil Parsons

      Thanks Ben, and others who have commented.

      I’ve not given up on the series of posts but unfortunately circumstances changed and I have a lot of other work taking up my time. I’ll do my best to add an article on RethinkDB this month!

      Reply

  8. ramusesan

    if you upload code it would be better for referrence to us

    Reply

Trackbacks & Pingbacks

  1. Real time data flow with Redux and Socket.io – Spraso – Redux

Leave a Comment

Your email address will never be published or shared and required fields are marked with an asterisk (*).