Building a chat room app with CycleJS and SocketIO — Part 1

This is part 1 of a 2-parts post that will show how to build a real world application using CycleJS and SocketIO. I recently wrote the same application but with ReactJS and RxJS. If you have not watched the CycleJS Egghead course by André Staltz, then I highly recommend you to do so first. I am building this based on the lessons from the above Egghead course.

In this part 1, we will accomplish the followings:

  1. We will stand up a real CycleJS application. In the Egghead course, Staltz showed all the samples in JSBin because the emphasis there was on the CycleJS concepts. In here, I will first show to make use of Babel, webpack and npm in building the application. No more including numerous JavaScript files in the index.html file like in years past. Using webpack and npm will result in a more modularized and modern JavaScript application.
  2. Incorporate MaterializeCSS. You might not be using this particular CSS framework, but once you see how it’s done with one, you can model the code to make it with others.
  3. From the get go, we will start planning to build the application like we do with ReactJS and with Lego toys, i.e. we make smaller components, then put them together to make bigger components and keep repeating that.
  4. We will make use of the Model View Intent pattern.

At the end of part 1, the application will look like

Image for post
Image for post

Nothing too exciting here yet. But like in a real chat application, there’s a text input where users can type. To send the text, users first type some text, and then can either click on the Send button or press the Enter/Return key. Regardless of either way, the text entry must then be blanked out while the focus remains on the text entry. This sounds like a simple and no brainer thing, but not doing so will fail the application.

Stand up the application

So first, let’s do a bunch of npm install commands. First the “compile-time” packages:

npm install webpack -g
npm install webpack --save-dev
npm install babel-core babel-loader babel-preset-es2015 babel-preset-stage-0 --save-dev

Then the “run-time” packages:

npm install rx @cycle/core @cycle/dom --save

Before going on, here is the structure of how I organize the files:

Image for post
Image for post

Inside the src folder is where we will be writing all the CycleJS files. The dist folder will contain the generated bundle.js file.

By the way, I hide the files that are specific to my environment (.gitignore, etc) by covering them in red.

Now we can write the index.html file which includes the necessary CSS files. It only contains a single div element called app.

Since we just installed babel and webpack as shown above, we will now be able to write CycleJS codes and import them to main.js, and when we run webpack command, everything will be bundled into the bundle.js which will be generated in the dist folder.

So, here’s the webpack.config.js file:

Static virtual DOM

It’s time now to write the main.js file. If we refer back to the screenshot above, we will structure the application UI as having 4 components. The first one is the appBar which is at the top. The second is the chatPane at the left and it contains the chat messages. The third is the presencePane at right pane and it shows active users in the chat room. The 4th and last is the textEntry area that contains a text input and a Send button.

In this part 1, we will leave out all the socketIO related code so the chatPane and presencePane will look empty. That’s OK because we will add them in part 2.

What I would do next is to generate the static virtual DOM tree. This static virtual DOM tree will have all the 4 components above, each in its own file as a building block.

The entry point is main.js and it’s location will be in the src directory. This is how it looks like:

First, we import all the necessary functions from the Rx and Cycle library in lines 1 through 3. Then we import the functions that creates the virtual DOM tree for the 4 components from lines 4 through 7. Eventually, we will create the intent() and model() functions, but right now since we’re only going after creating the static virtual DOM tree, we just create the view() function. The code in this view() function is hyperscript and, as opposed to JSX in ReactJS, is totally legal JavaScript. One can make the argument that it’s easier to read than JSX. If you look from lines 16 through 29, the intention is clear.

As a side note, an alternative and shorter way to specify the class attribute in CycleJS hyperscript is

div(‘.row’, [
div(‘.col .s6’, [
h4(‘Chat Messages’),
textEntryView$,
]),
div(‘.col .s6’, [
presencePaneView$,
]),
])

There is also another point to remember. The name of the function main() is arbitrary, but run comes from cycle core.

The source code for the view() function for appBar, chatPane, presencePane, and textEntry can be viewed here.

At this point, what we get when we run the application is the page that looks like the one the screenshot above, but it doesn’t do anything yet.

Add Intents

Now we’ll add the intents. The intents are the action that comes from either the Send button click event or the Enter key pressed event. So in the textEntry.js, we’ll add and export a couple of intent functions:

Then to consume it in main.js

In here, we also add the model() function, so model, view and intent are finally all present.

When we create the 2 intent function in textEntry.js, we want the future text stream coming from the text input where the users type. But instead of mapping to e.target.value like this

const textStream$ = DOMSource.select(‘#input-msg’).events(‘keyup’).map(e => e.target.value);

and like this

const textStream$ = DOMSource.select(‘#input-msg’).events(‘keyup’).filter(textStream => textStream.keyCode !== 13).map(e => e.target.value);

we just to keep it a little more generic, because later we will need the target to set the focus, i.e. keep the focus on the text input upon users clicking the send button.

In main.js, we use the Rx operator withLatestFrom, and then we merge the 2 observables (one from send button click and one from enter key pressed) using the merge operator.

The view so far does not depend on the model, hence the view() function does not take the model as an argument. This will change we incorporate the socketIO observables.

You also see a function called EffectHttp() here. This function handles the side effects such as subscribing to the text stream (typed by users in the text input) and take the text value and will make the HTTP post. This side effect handler is also responsible for blank out the text input element and set the focus on it.

The code for part 1 can be found here.

Here’s a demo on codepen (have to defeat the purpose of modules so everything fit in codepen, concept and most code stays the same)

Stay tuned for part 2.

Written by

Driven by passion and patience. Read my shorter posts https://dev.to/codeprototype (possibly duplicated from here but not always)

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store