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!
Key Takeaways
- Reactive Programming Benefits: Emphasizes the shift to Cycle.js due to its robust handling of asynchronous data streams, simplifying the creation and management of dynamic user interfaces with a unified codebase.
- Framework Efficiency: Cycle.js offers a more streamlined and functional approach compared to React, particularly in managing large codebases and complex data flows without relying on additional libraries like Redux.
- Handling Side Effects: Cycle.js utilizes drivers to manage side effects directly within the framework, providing a standardized, simpler method compared to React’s third-party dependencies.
- Functional Programming Paradigm: Cycle.js strictly adheres to functional programming principles, which facilitates easier testing and maintenance, and avoids the pitfalls of object-oriented programming prevalent in React.
- Community and Learning Curve: Acknowledges the smaller community and steeper learning curve as potential drawbacks for Cycle.js, but also notes the growing support and comprehensive documentation available.
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.
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.
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/dom – a collection of drivers that work with DOM; it has a DOM driver and HTML driver, based on the snabdom virtual DOM library
- @cycle/history – a driver for the History API
- @cycle/http – a driver for HTTP requests, based on superagent
- @cycle/isolate – a function for making scoped dataflow components
- @cycle/jsonp – a driver for making HTTP requests through JSONP
- @cycle/most-run – a
run
function for apps made withmost
- @cycle/run – a
run
function for apps made withxstream
- @cycle/rxjs-run – a
run
function for apps made withrxjs
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:
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!
Frequently Asked Questions about Switching from React to Cycle.js
What are the main differences between React and Cycle.js?
React and Cycle.js are both JavaScript libraries used for building user interfaces, but they have different philosophies and approaches. React uses a declarative programming model and a virtual DOM to manage changes in the application state. On the other hand, Cycle.js uses a reactive programming model and a virtual DOM driver to manage changes. This means that in Cycle.js, everything is a stream and the application state is managed by observing these streams and reacting to changes.
Why would I consider switching from React to Cycle.js?
Cycle.js offers a different approach to building user interfaces that some developers find more intuitive and easier to reason about. Its reactive programming model can make it easier to manage complex state changes and side effects. Additionally, Cycle.js is smaller and lighter than React, which can result in faster load times and better performance in some cases.
How difficult is it to switch from React to Cycle.js?
The difficulty of switching from React to Cycle.js largely depends on your familiarity with reactive programming and your willingness to learn a new paradigm. If you’re already comfortable with reactive programming, you may find the switch relatively straightforward. However, if you’re new to reactive programming, there may be a learning curve as you get used to thinking in terms of streams and observables.
Can I use existing React components in Cycle.js?
Yes, you can use existing React components in Cycle.js through a library called cycle-react. This library provides a way to wrap React components so they can be used in a Cycle.js application. However, keep in mind that this approach may not fully leverage the benefits of Cycle.js’s reactive programming model.
How does state management in Cycle.js compare to React?
In React, state management is typically handled through a combination of component state and context, or through external libraries like Redux or MobX. In Cycle.js, state management is handled through observables and streams. This can make state management in Cycle.js more predictable and easier to reason about, especially in applications with complex state changes.
How does Cycle.js handle side effects?
Cycle.js handles side effects through drivers. Drivers are functions that handle side effects such as DOM manipulation, HTTP requests, and user input. This approach isolates side effects from the rest of your application, making them easier to manage and test.
What are the performance implications of switching to Cycle.js?
Cycle.js is smaller and lighter than React, which can result in faster load times and better performance in some cases. However, the performance of your application will also depend on other factors such as the complexity of your application and the efficiency of your code.
How does testing in Cycle.js compare to React?
Testing in Cycle.js can be simpler and more straightforward than in React due to its reactive programming model and the isolation of side effects through drivers. This can make it easier to write tests that are deterministic and easy to reason about.
What resources are available for learning Cycle.js?
The official Cycle.js website provides a wealth of resources for learning Cycle.js, including a getting started guide, API documentation, and examples. There are also numerous tutorials and blog posts available online that cover various aspects of Cycle.js.
Is Cycle.js a good fit for my project?
Whether Cycle.js is a good fit for your project depends on a variety of factors, including the complexity of your application, your team’s familiarity with reactive programming, and your performance requirements. If you’re considering Cycle.js, it may be worth building a small prototype to get a feel for the library and see if it meets your needs.
I am Ivan, senior full-stack developer. I am developing and maintaining some awesome apps built using full stack JS, leading teams, mentoring and writing awesome code. I love JS and I am writing it every day. I love to experiment with new technologies and paradigms, such as reactive programming, DDD and new JS frameworks. I am very passionate about development and I am learning new things every day. In my spare time, I love to play guitar and ride a bike.