It is not hard to guess that most developers are now using any frameworks to develop applications. They help us structure complex applications and save time. Every day one can observe a large number of discussions, which framework is better, which one should be taught, and so on. So, this time I will share my experience and answer the question: “Why did I switch to Cycle.js from React?”.
React is perhaps the most popular frontend framework (at the time of 2017) with a huge community. I am a big fan of this framework and it helped me to change the view on web applications and their development. Some love him, and someone thinks he's not so good.
Most use React without thinking that there is a better tool or method for developing web applications. This gave me an impetus to try Cycle.js, as a new reactive framework, which is becoming more and more popular day by day.
map
or filter
. These functions return new threads that can also be used.
Reactive programming provides abstractions, and this makes it possible to concentrate on business logic.
There are a couple of great libraries for working with data streams in Javascript. One of them is the well-known Rx-JS extension ReactiveX, an API for asynchronous programming with monitored data streams. You can create an Observable (data stream) and manage it with a variety of functions.
The second library is Most.js. It has better performance, as evidenced by tests .
Also worth noting is one small and fast xstream library written by the author Cycle.js. It contains 26 methods and weighs 30kb. This is one of the fastest libraries for reactive programming on JS.
Just the examples for this article use the xstream library. Cycle.js was created to be small frameworks and I want to use the lightweight library together with Cycle.js.
Cycle.js is a functional and reactive Javascript framework. It provides abstractions for applications in the form of a pure main()
function . In functional programming, functions must take parameters as input and return something without side effects. In Cycle.js, the main()
function accepts parameters from the outside world and writes it to the outside world too. Side effects are realized with the help of drivers. Drivers are plugins that control DOM, HTTP requests, web sockets, and so on.
run()
with two arguments:
run(app, drivers);
app
is the main clean function, and drivers
are the above drivers.
run
-function for working with most
run
function to work with xstream
run
function for working with rxjs
index.html
and main.js
index.html
will contain only the main file with scripts, where all the logic is registered.
package.json
, so we’ll run
npm init -y
npm install @cycle/dom @cycle/run xstream --save
@cycle/dom
, @cycle/xstream-run
, and xstream
. We also need babel
, browserify
and mkdirp
, install them:
npm install babel-cli babel-preset-es2015 babel-register babelify browserify mkdirp --save-dev
.babelrc
file in the root of the directory with the following contents:
{ "presets": ["es2015"] }
package.json
"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'" }
npm run start
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>
div
with the main
identifier. Cycle.js will contact this element and render the entire application into it. We also included a dist/main.js
file. This is a jas-file transported through the babel, which will be created from the main.js
file.
main.js
and import all necessary dependencies:
import xs from 'xstream'; import { run } from '@cycle/run'; import { div, button, p, makeDOMDriver } from '@cycle/dom';
xstream
, run
, makeDOMDriver
and functions that will help us work with Virtual DOM ( div
, button
and p
).
main
function:
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') });
main
function. It takes the input parameters and returns the result. Input parameters are DOM streams (DOM streams), and the result is Virtual DOM. Let's start the explanation step by step:
const action$ = xs.merge( sources.DOM.select('.decrement').events('click').map(ev => -1), sources.DOM.select('.increment').events('click').map(ev => +1) );
action$
(here we use the "$" suffix convention for those variables that represent the data stream). One of the streams is a click on the button ( .decrement
) decrementing the counter by one, the second - on the other, increasing the counter, the button ( .increment
). We associate these events with the numbers -1
and +1
, respectively. At the end of the merge, the action$
flow will be as follows:
----(-1)-----(+1)------(-1)------(-1)------
count$
const count$ = action$.fold((acc, x) => acc + x, 0);
fold
function is perfect for this purpose. It takes two arguments: accumulate
and seed
. seed
is first emitted until the event comes. The next event is combined with the first, based on the accumulate
functions. This is practically a reduce()
function reduce()
for threads.
count$
stream gets 0 as the initial value, then for each new value from the action$
stream, we add up with the current value of the count$
stream.
run
function under the main
.
const vdom$ = count$.map(count => div([ button('.decrement', 'Decrement'), button('.increment', 'Increment'), p('Counter: ' + count) ]) );
count$
stream and return the Virtual DOM for each item in the stream. Virtual DOM contains one div
wrapper, two buttons, and a paragraph. As you can see, Cycle.js works with the DOM using the JS functions, but JSX can also be used .
main
function, we need to return our Virtual DOM:
return { DOM: vdom$, };
main
function and the DOM driver that is connected to and get the event flow for this div
element. We complete our cycle and create a great Cycle.js application.
That's all! This way you can work with DOM threads. If you want to see how to work with HTTP streams in Cycle.js, I wrote an article [eng] about it.
All code can be found on the GitHub repository .
Now you understand the basic principles of reactive programming and have already seen a simple example on Cycle.js, so let's talk about why I will use this bundle for my next project.
When developing web applications, managing a huge amount of code and data from various sources is a big problem. I am a React fan and I had many projects, but React did not solve all my problems.
React performed well when it came to rendering small data and changing the state of an application. In fact, its component methodology is awesome and really helps to write high-quality, supported and tested code. But all the time something was missing.
Let's look at the pros and cons of using Cycle.js instead of React.
When the application becomes large, problems arise when using React. Imagine that we have 100 components inside 100 containers, and each of them has its own functionality, tests and styles. This is a lot of lines of code inside multiple files inside a heap of directories. In this case, it is difficult to switch between all these files.
For me, the biggest problem React is the data flow. React was not originally designed to work with data streams, and this is not in the React core. The developers tried to solve this, and we have a lot of libraries and methodologies that solve this problem. The most popular is Redux. But he is not perfect. You need to spend a lot of time to configure it and you need to write a lot of code, which allows you to simply work with data streams.
this
, which causes a headache if something goes wrong.
Source: https://habr.com/ru/post/332674/