🤯 50% Off! 700+ courses, assessments, and books

Why I’m Switching from React to Cycle.js

Ivan Jovanovic
Share

Why I'm switching to Cycle.js - cyclists on bicycles

I would guess that most developers these days are using some sort of framework for developing apps. Frameworks are there to help us structure complex apps and save us time. Every day, we can see much discussion about which framework is the best, which framework should you learn first, etc. So today, I would like to share my experience, and why I’m switching to Cycle.js from React.

React is probably the most popular frontend framework these days and it has a great community. I am a big fan of it and it really helped me to change the way I think about web apps and how I develop them. Some developers love it, and some think that it’s not as good as everyone says.

Most people start to use React without thinking that there might be a better way to build a web app. That reflection made me try Cycle.js, a new reactive framework that is becoming more popular every day. In this article, I want to explain what reactive programming is, how Cycle.js works, and why I think it’s better than React. So let’s start!

What is Reactive Programming?

Reactive programming (RP) is programming with asynchronous data streams. If you’ve already built a web app, you probably did a lot of reactive programming. As an example, click events are asynchronous data streams. We can observe them and perform some side effects. The idea behind RP is to give us an ability to create data streams from anything and manipulate with them. We then have the same abstraction for all our side effects which is easier to use, maintain, and test.

You’re probably thinking “why do I need this new reactive programming thing?” The answer is simple: Reactive programming will help you unify your code and make it more consistent. You won’t need to think about how the things should work and how to properly implement them. Just write the code in the same way, no matter what data you work on (click events, HTTP calls, web sockets…). Everything is a stream of data and each stream has many functions that you can use to work with it, such as map, and filter. These function will return new streams that can be used, and so on.

Reactive programming gives you the bigger abstraction of your code. It will give you an ability to create interactive user experiences and focus on business logic.

reactive-clicks
Image taken from https://gist.github.com/staltz/868e7e9bc2a7b8c1f754

Reactive Programming in JavaScript

In JavaScript, we have a couple of awesome libraries for dealing with data streams. The most well-known one is RxJS. It’s an extension of ReactiveX, an API for asynchronous programming with observable streams. You can create an Observable (a stream of data), and manipulate it with various functions.

The second one is Most.js. It has the best performance and they can prove that with some numbers: Performance comparation.

I would also like to mention one small and fast library, made by the creator of Cycle.js and made specifically for it. It’s called xstream. It has only 26 methods, is approximately 30kb, and is one of the fastest libraries for reactive programming in JS.

In the examples below, I will use xstream library. Cycle.js is made to be a small framework and I want to attach the smallest reactive library to it.

What is Cycle.js?

Cycle.js is a functional and reactive JavaScript framework. It abstracts your application as a pure function, main(). In functional programming, functions should have only inputs and outputs, without any side effects. In Cycle.js’s main() function, inputs are read effects (sources) from the external world and outputs (sinks) are write effects to the external world. Managing side effects is done using drivers. Drivers are plugins that handle DOM effects, HTTP effects, and web sockets etc.

Cycle.js
Image taken from Cycle.js website

Cycle.js is there to help us build our user interfaces, test them and write reusable code. Each component is just one pure function that can run independently.

The core API has just one function, run.

run(app, drivers);

It has two arguments, app and drivers. app is the main pure function and drivers are plugins that need to handle side effects.

Cycle.js separates additional functionality into smaller modules. They are:

Cycle.js Code

Let’s see some Cycle.js code? We will create a simple app that should demonstrate how it works. I think that a good old counter app should be ideal for this example. We will see how handling DOM events and re-rendering the DOM works in Cycle.js.

Let’s create two files, index.html and main.js. index.html will just serve our main.js file, where the whole of our logic will be. We are also going to create a new package.json file, so run:

npm init -y

Next, let’s install our main dependencies:

npm install @cycle/dom @cycle/run xstream --save

This will install @cycle/dom, @cycle/xstream-run, and xstream. We are also going to need babel, browserify and mkdirp so let’s install them:

npm install babel-cli babel-preset-es2015 babel-register babelify browserify mkdirp --save-dev

For working with Babel, create a .babelrc file with this content:

{
  "presets": ["es2015"]
}

We’ll also need to add scripts to our package.json for running our app:

"scripts": {
  "prebrowserify": "mkdirp dist",
  "browserify": "browserify main.js -t babelify --outfile dist/main.js",
  "start": "npm install && npm run browserify && echo 'OPEN index.html IN YOUR BROWSER'"
}

For running our Cycle.js app we’ll use npm run start.

That’s all. Our setup is done and we can start writing some code. Let’s add some HTML code inside index.html:

< !DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8"/>
    <title>Cycle.js counter</title>
</head>
<body>
    <div id="main"></div>
    <script src="./dist/main.js"></script>
</body>
</html>

We’ve created a div with an id of main. Cycle.js will connect to that div and render the whole app within it. We’ve also included thedist/main.js file. That’s the transpiled and bundled JS file that will be created from main.js.

It’s time to write some Cycle.js code. Open the main.js file and import all the dependencies we need:

import xs from 'xstream';
import { run } from '@cycle/run';
import { div, button, p, makeDOMDriver } from '@cycle/dom';

We are including xstream, run, makeDOMDriver and functions that will help us work with Virtual DOM (div, button and p).

Let’s write our main function. It should look like this:

function main(sources) {
  const action$ = xs.merge(
    sources.DOM.select('.decrement').events('click').map(ev => -1),
    sources.DOM.select('.increment').events('click').map(ev => +1)
  );

  const count$ = action$.fold((acc, x) => acc + x, 0);

  const vdom$ = count$.map(count =>
    div([
      button('.decrement', 'Decrement'),
      button('.increment', 'Increment'),
      p('Counter: ' + count)
    ])
  );

  return {
    DOM: vdom$,
  };
}

run(main, {
  DOM: makeDOMDriver('#main')
});

This is our main function. It gets sources and returns sinks. Sources are DOM streams and sinks is the virtual DOM. Let’s start by explaining part by part.

const action$ = xs.merge(
  sources.DOM.select('.decrement').events('click').map(ev => -1),
  sources.DOM.select('.increment').events('click').map(ev => +1)
);

Here we are merging two streams into a single stream called action$ (it’s convention to suffix the name of variables that contain streams with a $). One is a stream of clicks on decrement and other on increment button. We are mapping those two events to the numbers -1 and +1, respectively. At the end of the merge, the action$ stream should look like this:

----(-1)-----(+1)------(-1)------(-1)------

The next stream is count$. It’s created like this:

const count$ = action$.fold((acc, x) => acc + x, 0);

The fold function is great for this purpose. It accepts two arguments, accumulate and seed. seed is firstly emitted until the event comes. The next event is combined with the seed based on accumulate function. It’s basically reduce() for streams.

Our count$ stream receives 0 as the starting value, then on every new value from the action$ stream, we are summing it with the current value in count$ stream.

At the end, to make the whole circle work, we need to call the run function below main.

The last thing is to create the virtual DOM. Here’s the code that does that:

const vdom$ = count$.map(count =>
  div([
    button('.decrement', 'Decrement'),
    button('.increment', 'Increment'),
    p('Counter: ' + count)
  ])
);

We are mapping the data in the count$ stream and returning a virtual DOM for each item in the stream. The virtual DOM contains one main div wrapper, two buttons, and a paragraph. As you see, Cycle.js is using JavaScript functions to work with the DOM, but JSX can also be implemented.

At the end of the main function, we are returning our Virtual DOM:

return {
  DOM: vdom$,
};

We are passing our main function and a DOM driver that’s connected to the div with the ID main and getting the stream of events from that div. We are closing our circle and making the perfect Cycle.js app.

This is how it works:

Animated GIF demonstrating the counter being incremented and decremented

That’s it! This is how you work with DOM streams. If you want to see how HTTP streams work in Cycle.js, I’ve written article about that (on my blog)[http://ivanjov.com/working-with-http-streams-with-cycle-js/]

I’ve pushed all code to a Github repo. Check it and try to run it on your local machine.

Why Am I Switching from React to Cycle.js?

Now that you understand the basic concepts of Reactive programming and have seen a simple example in Cycle.js, let’s talk about why I will be using it for my next project.

The biggest problem I’ve had when designing web apps is how to handle large codebases and large amounts of data coming from different sources. I am a fan of React and I’ve used it in many projects, but React didn’t solve my problems.

When it comes to rendering some data and changing app state, React works very well. In fact, the whole component methodology is amazing and it really helped me to write better, testable, and maintainable code. But something was always missing there.

Let’s see some pros and cons of using Cycle.js over React.

Pros

1. Big codebases

React has some issues when your app becomes big. Imagine that you have 100 components inside 100 containers and each of them has it’s own styles, functionality, and tests. That’s a lot of lines of code inside many files inside many directories. You see what I mean here, it’s hard to navigate through these files.

Cycle.js helps us here. It’s designed to handle large codebases by splitting the project into independent components that can be isolated and tested without side effects. No Redux, no side effects, everything is a pure data stream.

2. Data flow

The biggest problem I had in React is data flow. React is not designed with a data flow in mind, it’s not in React’s core. Developers have tried to solve this, we have many libraries and methodologies that try to deal with this issue. Most popular is Redux. But it’s not perfect. You need to spend some time to configure it and need to write a code that will just work with the data flow.

With Cycle.js, the creator wanted to create a framework that will take care of data flow because you shouldn’t have to think about it. You just need to write functions that do some operations with data and Cycle.js will handle everything else.

3. Side effects

React has issues with handling side effects. There is no standardized way to work with side effects in React apps. There are a lot of tools that help you deal with it, but that also takes some time to setup and learn how to use them. The most popular ones are redux-saga, redux-effects, redux-side-effects, and redux-loop. You see what I mean? There’s a lot of them… You need to choose the library and implement it in your codebase.

Cycle.js doesn’t require that. Simply include the driver you want (DOM, HTTP or some other) and use it. The driver will send the data to your pure function, you can change it and send it back to the driver that will render it or do something else. Most importantly, it’s standardized; that’s what comes with Cycle.js and you don’t need to depend on a third party library. So simple!

4. Functional programming

And last but not least, functional programming. React creators claim that React uses functional programming but that’s not really true. There is a lot of OOP, classes, use of the this keyword that can give you headaches if not used properly… Cycle.js is built with the functional programming paradigm in mind. Everything is a function that doesn’t depend on any outer state. Also, there are no classes or anything like that. That’s easier to test and maintain.

Cons

1. Community

Currently, React is the most popular framework and it’s used everywhere. Cycle.js isn’t. It’s still not very popular and this can be a problem when you come across some unplanned situation and can’t find a solution to an issue in your code. Sometimes you can’t find an answer on the internet and you are left on your own. This is not a problem when you work on some side project and have plenty of free time, but what happens when you work in a company with a tight deadline? You will lose some time debugging your code.

But this is changing. Many developers are starting to use Cycle.js and talking about it, about problems and working on together on solving them. Cycle.js also has good documentation with a lot of examples and, so far, I haven’t had any complicated problem that was too hard to debug.

2. Learning a new paradigm

Reactive programming is a different paradigm and you will need to spend some time getting used to how things are done. After that, everything will be easy, but if you have a tight deadline then spending time learning new stuff can be a problem.

3. Some apps don’t need to be reactive

Yeah, some apps really don’t need to be reactive. Blogs, marketing websites, landing pages and other static websites with limited interactivity don’t need to be reactive. There is no data that goes through the app in the real-time, not so many forms and buttons. Using a reactive framework will probably slow us down on this websites. You should be able to assess if a web app really needs to use Cycle.js.

Conclusion

An ideal framework should help you focus on making and delivering features and should not force you to write boilerplate code. I think that Cycle.js has shown us that this is really possible and forces us to look for better ways to write our code and deliver features. But remember, nothing is perfect and there is always room for improvement.

Have you tried reactive programming or Cycle.js? Have I convinced you to give it a try? Let me know what you think in the comments!

This article was peer reviewed by Michael Wanyoike. Thanks to all of SitePoint’s peer reviewers for making SitePoint content the best it can be!