I’ve used Angular (v.2+) fairly extensively over the last year and a half. I have some misgivings about the framework, but I really like that their team embraced observables. Any time I hit a point in Angular where I can just program with the RxJS library, it feels like a “moment of clarity.”
In an effort to become more productive with Angular, I’ve watched all of the RxJS tutorials on egghead.io from Andre Staltz. I noticed he created his own stream-oriented framework called Cycle.js, with an introductory course sitting next to all of his RxJS tutorials on egghead. Cycle.js has been on my to-learn wishlist for maybe a year now.
I had been using nootropiccattreats.win as an over-engineered random quote generator to test Angular related concepts and practices. I rebuilt it using Cycle.
Conceptually, Cycle.js is based on inputs and outputs that feed back and forth. It’s partly inspired by human-computer interaction, with a computer producing an output that a human perceives as an input and takes some action for the computer to again process and for the person to again react to.
The documentation offers a pattern called Model View Intent (MVI) as a way to organize code. Under this paradigm, “model” is code that processes information, “intent” is code that listens for user activity, and “view” is code that specifies what the user should see.
Here’s the bulk of the code for my random quote generator. The “intent” block watches for click events. The “model” block tracks state. The “view” block renders the DOM using a templating language that I think is a custom version of hypersrcipt.
The fold
function is a method from xstream. It’s kind of like reduce
for arrays. I’m using it: to start with the value of random(0, quotesLength, 0)
on initial load of the application, to produce a new state when appropriate (return random(0, quotesLength, prev)
), and to run a couple side effects (cur.target.blur(); window.scrollTo(0, 0);
).
I think of fold
as xstream’s scan
and startWith
pattern. I have a memory of the redux documentation saying that you could replace all of redux with the scan
and startWith
RxJS operators (though I couldn’t find this on the site when I searched for it recently).
import { button, div, h, p, span } from '@cycle/dom';
import xs from 'xstream';
import random from '../util';
import quotes from '../quotes';
// interpret what the user wants to do for the model
function intent(domSource) {
const newClick$ = domSource.select('.new').events('click');
return { newClick$ };
}
// model represents internal app state
function model(actions) {
const quotesLength = quotes.length - 1;
const { newClick$ } = actions;
return newClick$
.fold(
(prev, cur) => {
cur.target.blur();
window.scrollTo(0, 0);
return random(0, quotesLength, prev);
},
random(0, quotesLength, 0)
)
.map((n) => quotes[n]);
}
// views render state
function view(state$) {
return state$.map((quoteObj) =>
div(`.container.${quoteObj.length}`, [
span('.quotemark-l', '“'),
h('blockquote', { props: { innerHTML: quoteObj.quote } }),
div('.meta', [
div('.cite', [
p('.person', quoteObj.quotee),
p([h('a', { props: { href: quoteObj.link } }, quoteObj.platform)])
])
]),
div('.container-button', [button('.new', 'new quote')])
])
);
}
export default function (sources) {
const actions = intent(sources.DOM);
const state$ = model(actions);
const vdom$ = view(state$);
const sinks = {
DOM: vdom$
};
return sinks;
}
A random quote generator is not a good test of the quality of an application framework. But for the rest of this piece, I’m going to kind of sort of make claims about the quality of Angular and Cycle as application frameworks based off of a random quote generator.
I can tell you that I find the development time of building a random quote generator in Cycle to be much, much shorter than Angular + NgRX, but building a random quote generator in vanilla JavaScript would be the quickest of all.
Cycle’s API is small and flexible enough that using it for a random quote generator felt kind of natural. Using Angular with NgRX feels ridiculous, and not just by comparison. The overhead it brings of spreading out components and a reducer and wiring everything together and debating whether you should actually use the type features of TypeScript makes Angular feel inappropriate on any small and most medium-sized projects, in my opinion.
Like Angular and React, Cycle has its own CLI called create-cycle-app
, but I ejected pretty quickly because it wasn’t clear from the documentation what create-cycle-app
was using: whether it was running webpack (though I mean, of course it was), whether it would process my CSS, what testing framework would run with npm run test
, etc.
I think I spent the most time trying to figure out how to use the templating language. Cycle ships with what I think is a custom implementation of hypersrcipt. I couldn’t find much documentation on the subject, so I used the documentation for hyperscript and just kind of guessed at what I could do. It wasn’t that difficult, but it stands out in my mind because every other step in the process seemed straightforward. Cycle is also compatible with JSX if you install a couple extra npm modules, but I wanted to learn new things.
My initial reaction when asking myself whether I’d use Cycle.js at work was, “no” for the following reasons:
I’m rethinking these reactions.
I can’t think of any other developers I know here, in real life, that have even tried to use Angular. Most use React, or have a large codebase in AngularJS that they’re researching on how to best refactor as a React application. It doesn’t seem likely that the next front-end developer we hire is going to be an Angular specialist. In such a circumstance, it seems like a smaller, simpler API would be easier to mentally parse with a human brain. Cycle has a tiny API compared to Angular.
It doesn’t seem logical to assume that at some point of complexity, x, Angular is suddenly the cleanest tool to use and Cycle.js tips out of control, and yet that’s what I’m scared of. I can imagine that many of the tools Angular provides start to pay off on large scale, large team projects, but I can also imagine the problems I tend to have with Angular just multiply with more people, rather than suddenly propagating genius code structure into our collective minds.
I find being a programmer humbling. There’s always a reminder that someone else is out there who’s much smarter than me. All I have to do is open my node_modules
directory. The fact that Angular and React are backed in some capacity by Google and Facebook is comforting to me. Just by using Angular, I feel my code is “more correct.” This seems like a dangerous mindset.
My JS Written | Minified JS | |
---|---|---|
Angular + NgRX | ~250 lines | 381KB |
Cycle.js | ~80 lines | 109KB |
If you like RxJS, Cycle.js should have a very low learning curve, even if you use xstream instead of RxJS. Andre Staltz’s course, Cycle.js Fundamentals, is available free on egghead, and it’s all you probably need to get started. I’m cautiously optimistic on how well Cycle would scale.