
Cycle.js – A functional and reactive JavaScript framework for predictable code - octosphere
https://cycle.js.org/
======
pickle-ts
I've used cycle.js on a small commercial project. I'm drawn to novel non-
mainstream approaches, and am happy to put up with teething pains with new
technology. However, my experience with cycle.js was such that I couldn't
recommend the framework to others.

Central to the understanding of cycle.js is the concept of observables. An
observable is powerful abstraction, as it allows event streams to be
programmable. However, an overreached abstraction can be even worse than a
lack of abstraction. Cycle.js gratuitously applies the observable abstraction
to its component model.

The cycle.js notion of a component is a object that takes multiple observable
sources, and emits multiple observable sinks. I have to admit, at first I
didn't see this as problematic: to the contrary - it actually struck me as
elegant. What I didn't realize, before building a real-world project with
cycle, was that this meant all interactions between components were
_asynchronous_. So each component cannot even access the state of another
component without communication via observables. This introduces enormous
needless complexity into an application. Not only do you have to pay an up
front mandatory boilerplate tax to minimally wire together your components,
you'll tie yourself in knots readjusting that wiring to get those components
to communicate with each other in the way you need.

We actually ended up re-writing the project with our own framework, pickle:
[https://github.com/pickle-ts/pickle](https://github.com/pickle-ts/pickle) For
us, this substantially increased maintainability / reduced the code size of
our application compared with cycle.js, or the prototype we wrote with
react/redux. The code reduction came from realizing the appropriate
abstraction: the entire application should be composed of an object-oriented
hierarchy of stateful components, where on each update, these mutable
components render themselves as immutable v-dom trees. Anyway, to counteract
any pluggishness (our framework is obviously super new so is only of interest
to ultra early-adopters), the conservative sensible choice for a web framework
right now is to use react or vue /w a state manager. Not cycle.js.

~~~
staltz
I suppose you were using Cycle.js before Cycle State was released. There are a
few other projects in production like yours, some of them successfully
applying the framework, and others that found difficulties with state
management and wiring between components.

For anyone out there looking to evaluate Cycle.js for production, I recommend
looking at the source code of an open source app I'm actively building:
[https://github.com/staltz/manyverse](https://github.com/staltz/manyverse) I
am happy with the architecture, specially how well it manages React Native's
many native APIs in a well organized way.

I also need to emphasize that Cycle.js is more of an architectural framework,
not much of a rendering framework, so it's not a competitor to React, you can
use both together. React for rendering, and Cycle.js for architecture and
orchestration.

~~~
pickle-ts
_I suppose you were using Cycle.js before Cycle State was released_

No, I was using cycle state/onionify.

 _[cycle-js] is not a competitor to React_

That's not the impression you give in this article: [https://staltz.com/some-
problems-with-react-redux.html](https://staltz.com/some-problems-with-react-
redux.html)

 _React may be superior in ecosystem, or with feature coverage, but not so as
a paradigm [compared to cycle-js]... Once you learn Elm or Cycle, getting
things done will be more productive, less indirect, less verbose, more
organized._

Am I quoting you fairly? Have you changed your position?

~~~
staltz
That article was written many months/years before Cycle-React interop was
built. I still maintain some of those positions because of Redux, but when you
take React without Redux, it's just a rendering library, and as such could be
theoretically used with Cycle.js. Now that React has good APIs like Context,
ForwardRef, and function components, the interop with Cycle.js is easy and
well done.

~~~
pickle-ts
FWIW I agree with you I'm not enamoured by react/redux.

~~~
WorldMaker
redux-observable has helped me get some of the better parts I liked about
Cycle into a more "traditional" React app setup.

For maintainability by other developers I've been migrating my biggest Cycle
app into a "traditional" React app. Partly because I got very caught up into
some "plumbing" issues in Cycle, with lots of dynamic components and keeping
them performative starting being far too much work, especially if I expected
other developers to read it, much less touch it. The Cycle/React-interop has
gotten better so that I might contemplate React-hosted Cycle components for
future needs, but at this point with the vast React ecosystem and increasing
Junior Developer-friendliness of "traditional" React, it makes the most sense
to just use as much off-the-shelf React as possible and Redux seems familiar
to more developers (even if they might not immediately get what redux-
observable "epics" are doing).

------
jakelazaroff
Cycle is really cool, and I'd love to see reactive programming become more
popular, but after trying it out for a project it has a few ergonomics issues
that keep me from using it as my primary web framework.

\- Dynamic lists of components are really annoying to create. In Cycle, the
happy path is calling every component function in your app at startup to set
up the data flow graph, and then your reactive operators handle the actual
logic. With dynamically added or removed components, you get a stream of
"sinks" and then have to splice them into your data flow somehow.

\- Circular dependencies are cumbersome. If a parent passes a value to a child
which can change that value in the parent, you essentially have to create a
stub stream in the parent, use it to derive the parent's state and then
"imitate" the output of the child as well.

That said, Cycle has a lot of great ideas that I want to become more
mainstream. If you have a free weekend, try it! The creator André Staltz's
blog is also a good read: [https://staltz.com/](https://staltz.com/)

~~~
staltz
I totally agree with the two issues you listed! I think both of them are well
enough addressed with Cycle State, a new state management library. It has an
API makeCollection() specifically meant for dynamic lists, with good DX and
great performance. Also I regret imitate(), and gladly using Cycle State I
haven't found a single need anymore for imitate().

Not mentioned in the official docs (yet), but Cycle.js has nowadays full
support for React DOM and React Native.

~~~
aarpmcgee
Is there JSX support?

~~~
jakelazaroff
Yup! [https://cycle.js.org/getting-started.html#getting-started-
co...](https://cycle.js.org/getting-started.html#getting-started-coding-
consider-jsx)

If you're just starting out though (or even if you're not) I'd recommend
checking out create-cycle-app, the Cycle analogue to create-cycle-app that
will set up things like that for you: [https://github.com/cyclejs-
community/create-cycle-app](https://github.com/cyclejs-community/create-cycle-
app)

~~~
aarpmcgee
Rad! Thanks!

------
XCSme
The code looks a bit messy to be honest, maybe it's just the variable naming.
`const input$ = sources.DOM.select('.field').events('input')` Why not name it
the same as the DOM function `querySelector` ? `const name$ = input$.map(ev =>
ev.target.value).startWith('')` Wouldn't in this case `name$` be a boolean,
but then `map` is called on a boolean. I just find the syntax very confusing.

~~~
typon
What makes you guess that `name$` is a boolean?

~~~
sritchie
I think the poster is assuming that .startWith(“) returns a Boolean, yeah?

~~~
coltonv
startWith, in this context of streams, means "let this value (in this case an
empty string) be the first value the stream returns"

------
bsimpson
The core idea of Cycle is that the user is a function that reads views and
emits user input events; while the application is a function that reads user
input events and emits views. (Hence, the name "cycle.")

I thought it was written up in their docs, but this video is what comes up
when I search for "user is a function":

[https://www.youtube.com/watch?v=1zj7M1LnJV4](https://www.youtube.com/watch?v=1zj7M1LnJV4)

This framing, that user input and interfaces react to changes in each other,
is a nice way to think about interaction design.

Ergonomically, I find virtual DOM composition in Cycle a bit too cumbersome.
Stateless components are just functions that return virtual DOM, but stateful
ones need the input/output streams woven in. In practice, this means you end
up doing a lot of

const statefulComponent1$ = // compose input streams here

return {

    
    
      DOM: combineLatest([
    
        statefulComponent1$, 
    
        statefulComponent2$
    
      ]).map(
    
        ([ partial1, partial2 ]) => (
    
          <StatelessComponent>
    
            { partial1 }
    
            // ...
    
          </StatelessComponent>
    
        )
    
      )
    

}

To add state to a stateless component, you've got to do a bunch of refactoring
to both the component and its callsite. Instead of passing props/function
arguments inline to a stateless component, now you've got to make a variable
to hold your stream, pass it into combineLatest, and put a slot in your
returned virtual DOM to hold its latest emission. I understand why it's this
way, but I also don't like having to think about if a component has state when
I'm just trying to use it.

Cycle is a nice architecture for Chrome extensions though. They do a lot of
message passing between JS contexts. Because everything in Cycle is a stream,
you can wrap the extension messaging API in a stream, and your extension
architecture looks like any other Cycle project:

[https://github.com/appsforartists/midicast/blob/develop/pack...](https://github.com/appsforartists/midicast/blob/develop/packages/cycle-
extensions/src/index.ts)

------
xrd
The thing that got me excited about React was (and please correct me if my
vocabulary is incorrect) the virtual Dom and the algorithm to detect changes
and only update the dom that changes. Does Cycle.js save that same work and
reduce bugs, or is there a hidden cost without that kind of feature?

To be specific, this reference criticises setState because it does a bunch of
implicit updates to subcomponents. I've seen how in practice this does make
things hard, but I've also seen the power of it when it works well. Cycle.js
looks like it requires me to do a lot more to manage that re-rendering with
the benefit of knowing exactly how it is happening.

It is a myth that what I've described above actually saves time and reduces
bugs?

I've loved reading Andre Staltz's posts on reactive programming, they are so
good. This is an amazing project.

Edit: clarified my comment to add example.

~~~
jakelazaroff
Cycle uses a virtual DOM as well. The difference is that React will call your
component functions every time it needs to render, while Cycle will (mostly)
only call them once, when the app starts up. A Cycle app is a data flow graph
set up by your component functions, with a "driver" at the root observing that
data flow and causing side effects, like rendering your virtual DOM or making
HTTP requests.

In short: instead of a component being called with props and state and
returning VDOM, in Cycle it "maps" a stream of state into a stream of VDOM.

------
kylecordes
Cycle is very interesting, and Andre is a genius. Even if you will never use
Cycle in "real work", learning it enough to build anything at all will enhance
your understanding of many things. Particularly around reactivity and state.

A while back I dug in enough to make a short series of videos explaining how
some simple Cycle apps work. They are not up to date with the Cycle progress
since then, but nonetheless are probably still a decent way to get a sense of
what Cycle is like.

[https://www.youtube.com/watch?v=DEHgcT_RmJc&list=PLP4qxCldB2...](https://www.youtube.com/watch?v=DEHgcT_RmJc&list=PLP4qxCldB2jRJmszTeuS1-zghi60g1vzJ)

------
lgessler
Can anyone comment on how Cycle.js compares to other frontend FRP frameworks
like ClojureScript's re-frame[1]?

[1]: [https://github.com/Day8/re-frame](https://github.com/Day8/re-frame)

~~~
lilactown
[NOTE: I have much more experience using re-frame than Cycle.js, but have used
both previously]

Cycle.js and re-frame, in some ways, are on opposite sides of various
spectrums.

Cycle.js favors local reasoning and composability through modeling your
application as a pure function that takes in streams (sources) and returns
streams (sinks). All of your event handling, external data, DOM changes, etc.
are explicitly tied together in your "main" function.

Cycle.js requires you to reason about, write and read your application code in
terms of reactive "streams" of values. This means that a big part of your
app's development is expressing it in terms of operations on reactive streams
and using the language of whichever stream library you choose.

Re-frame favors separating your UI code (which you can compose) from state and
other side effects (which are global) through it's use of named reactive
subscriptions and dispatching of named events.

You register your app's events as functions that take in "coeffects" (e.g. the
current state of your app) and returns "effects" as data, e.g. the new state
of your application. You can also return other "effects" like the parameters
for doing an HTTP request and what event to dispatch when it completes/fails.
Your UI code is then written to dispatch the names of these events (e.g. on
click) that will trigger the registered function and cause re-frame to run the
returned effects.

You register your app's subscriptions as functions that take in the
application state and return a materialized view of it. Your UI code is then
written as functions that subscribe to the names of these subscriptions, take
the materialized view of your app state and return the UI tree that should be
shown.

The majority of your re-frame app code is written as regular functions which
return data. This means that a big part of your app's development is
expressing it in the terms of functions that return data so that the UI
framework (React/reagent) can render it and re-frame can do the appropriate
side effects.

Cycle.js is a very "pure" framework, in that it's approach is to start from
first principles - pure functions, application-as-a-stream - and to layer on
tools and abstractions to make it effective. The work that the core of
Cycle.js actually does for you is quite minimal, and the skeleton and muscles
of your application is not at all hidden from you.

Re-frame is much less "pure"; it has a guiding concept of separating your UI
code from the side effects it produces. For instance, many UI functions are
not pure because they subscribe to the global app state. And in fact you don't
have to use re-frame for everything at all; you can opt to use local component
state and direct mutation instead of that fits your use case better.

~~~
vemv
That's super interesting. Imagine that as a cljs developer, Cycle and re-frame
were exactly as easy to use (i.e., skip the fact that Cycle is written in a
different language).

In that case which one would you prefer / why?

~~~
lilactown
The sticks I would use to measure a framework like Cycle and re-frame are:

1\. How well does it compose?

2\. How simple are the tools?

Both re-frame and Cycle have their issues with composition.

Cycle has inherent complexity and difficulty with composing dynamic child-
states and circular dependencies as outlined above. It sounds like there have
been tools developed since I last used Cycle.js to ameliorate the difficulties
in handling these cases.

Re-frame has complexity with composition because it uses an absolute global
namespace, which makes multi-tenant scenarios very awkward and potentially
insecure. This problem mainly exists in use cases like having a developer
environment with multiple states of the application on the same page,
consuming components developed by external teams, and server-side rendering.
At the moment these are either worked around or avoided completely.

The simplicity of the tools is something that I am very passionate about. As
someone infected by the Clojure virus, I am pretty excited about the ability
to use regular functions that return data to build my applications. It has
been several years since I went down the rabbit hole of "everything as a
stream/observable." At the time I found it to be very intellectually exciting,
but also very confusing. Functions and data are much more familiar tools to
most developers and the ergonomics of designing, developing, and debugging
functions and data are much more understood.

So in closing, I would choose re-frame over Cycle for a project that I was
working with a team of developers on. For extremely large applications that
needed to be developed cross-team and/or have a strong use-case for server-
side rendering... I would probably pick something new- similar to re-frame,
but which fixes it's issues with composition. I maaayyyyy be working on
something like that. ;)

------
inglor
Anyone considering cycle should also consider MobX which is effectively
"automatic cycle". It's as functional and as Reactive but uses vue-like
observables rather than rx-like ones.

------
JSDave
"const input$ = sources.DOM.select('.field').events('input')"

Is there a switchMap in here?

What happens if the input element is removed/inserted back into the DOM?

"input('.field', {attrs: {type: 'text'}}),"

It's possible right now for the value in this input to differ from the value
in input$.

Maybe use a combineLatest and then this?

"input('.field', {attrs: {type: 'text', value: inputVal}}),"

~~~
jvanbruegge
`select().event()` does not use `querySelector` directly under the hood, it
filtering and routing all incoming events accordingly. So even if you change
the elements, as long as they match the selector, they will be routed
correctly. Source: I recently rewrote the DOM driver :)

------
awaisraad
Man, I should start pasting my bookmarks on HN - that will give major boost to
my HN score.

~~~
todd3834
Please do, why not?

~~~
wild_preference
They were being dismissive, suggesting that this is old news and something
they already had on vinyl. As if all we care about is the new shiny and can't
possibly be interested in nor discuss existing things.

------
ww520
This looks not much different than the usual way of doing event binding and
rendering in the event callback.

~~~
gcb0
lol. yes. maybe the name means full-circle :)

~~~
z3t4
Give it a few more years and we'll see .ondblclick and event listeners.

------
k__
Cycle.js is what Angular2 should have been.

------
brucou
This is interesting feedback. There are obviously subjective parts in it, but
that's valuable all the same.

Focusing on the objective parts :

    
    
      An observable is powerful abstraction, as it allows event streams to be programmable. However, an overreached abstraction can be even worse than a lack of abstraction. Cycle.js gratuitously applies the observable abstraction to its component model.
    
    

Some caveats aside, cyclejs allows you :

\- to express your app as a series of equations

\- separate side-effects from pure dataflow processing

Whether that is gratuitous or overreached is a question of taste, familiarity
and other things, I won't discuss with you on that.

What I will say is that under some conditions, the cyclejs approach actually
can make your application much easier to understand that standard imperative
workflow processing. For instance, I could write for a super simplified
reservation system :

\- screen `=def=` booking_details_screen + booking_feedback_screen

\- booking_feedback_screen `=def=` is_ok(booking_response) ? nothing :
display_errors(booking_feedback_screen)

\- booking_details_screen `=def=` whatever interface you want

\- book_button_click `<-read_dom_effect-` = <provided by the DOM>

\- flight `=def=` read_flight (booking_details_screen)

\- booking_request `=def=` makeBookingRequest(flight) sampled by
book_button_click

\- booking_response `<-booking_effect-` booking_request

The `=def=` relations are equations which are always true. the `<-effect-`
relations express the causal relation between asking for a request to be
performed and the response resulting from that request.

On another note, as you can see from the equations, there is no notions of
component. Or if you prefer a component is a variable on the leftside of the
equations. So for instance screen is a component with two children components.

That is the underlying theory. In practice:

\- code does not get written as a list of equations, but through doing some
juggling with streams operators. Nothing of the other world really, but there
is certainly a ceremony to it, and tips, tricks, gotchas to be aware of.

\- Cycle actually does not have a component model stricto sensu. A component
model should at least provide a systematic way to combine components into a
hierarchy, i.e. a `combine` function by which `parentComponent` =
combine(childrenComponents)`. For instance in React, `reactElement =
React.createElement(parentComponent, props, childrenComponents)`. With
`cyclejs` you must explicitly write your `combine` function yourself,
everytime. That represent probably a portion of the pain you felt.

But conversely look at the other approach. Let's imagine a `ParentComponent`
with two child components. To reason about your application, you need to know
about the end-to-end sequence of operations involved in all the framework's
API. So for instance: `render = render Parent -then-> call onRender of Parent
-then-> render Child1 -then-> fetch data -then-> render Child2 -then-> etc.`.
This is obviously possible, that is how React works and your own proposed
framework too - by the way you do not offer a component model either, stricto
sensu). Nothing of the other world really, but there is certainly a ceremony
to it, and tips, tricks, gotchas to be aware of (yes I copy pasted from
before).

So in short, your evaluation of how bad the ceremony is, the effort it
involves etc. will obviously include factors which are specific to you. So I
am not doubting your feedback. My whole point here is to say that your mileage
may vary and that there is no a priori superiority of one approach over
another (stuff like `realizing the appropriate abstraction` as you mention in
your post). There are real pain points both ways and what is appropriate to
you might be in part the result of a higher familiarity with one approach over
another.

If you want to understand what I mean by component model, or want to see what
a component model for cyclejs look like, I created my cyclejs component model
to address some of the pain points you probably encountered : cf.
[https://brucou.github.io/posts/a-componentization-
framework-...](https://brucou.github.io/posts/a-componentization-framework-
for-cyclejs/)

------
apolymath
I am definitely an advocate for reactive programming, as I have used React
Native to develop a cross-platform mobile application
([https://dedicate.datasilk.io](https://dedicate.datasilk.io)), but when it
comes to web development, I am very much an advocate for using the basic tools
that were given to us web developers to build web applications.

These tools include mainly HTML & CSS, and when needed, "vanilla" JavaScript
(and maybe even a compact jQuery alternative like zepto), but in no way do I
advocate for using client-side frameworks for developing web applications. It
is because they are difficult to debug, sometimes near impossible, and also
because it leads the developer astray from practicing basic concepts such as
writing beautifully formatted HTML & CSS or utilizing small bits of JavaScript
for event handling & AJAX calls. Instead, they are forced to focus nearly all
of their attention to writing JavaScript in what I call a "black box"
environment where they send input into the black box and the magical sorcery
of said black box somehow outputs a graphical user interface.

A web application should harness the power of the web browser's native
capabilities instead of harnessing the powers of a JavaScript framework,
because the web is a beautiful place made of beautiful technologies that were
built in the golden era of technological creativity. Because of such
creativity, many great people came together and invented HTML, CSS, and
JavaScript, the near-perfect solution to a very complex, graphically
interactive problem.

Sure, you may think that you want to build a single-page application, and
that's fine. Why not test your skills and write a micro-library that harnesses
the HTML5 History API along with a simple AJAX method to transform your
website into that single-page app you fantasize about daily.

Sure, you may think using the JavaScript "import" keyword in all of your many
JavaScript files is okay because it's a single-page app that you're building
and all those thirty-some-odd JS files will only be loaded one time for each
user session when the app initializes, but what you don't understand is that
you are lazy and careless and on a daily basis, you make excuses for all of
your sins.

You and I both know that your application only needs ONE small JS file on
initialization and ONE dynamically loaded JS file for each area within the app
that your user may or may not navigate to in the first place.

So, for the love of all humanity, please, please, write HTML & CSS, and pepper
it ever-so-lightly with bits of JavaScript (but only when it is absolutely
necessary).

If you need a more convincing argument to steer away from client-side
JavaScript frameworks, simply read this small comic strip:
[https://xkcd.com/927/](https://xkcd.com/927/)

~~~
jakelazaroff
This is an unnecessarily absolutist position. If HTML, CSS and JavaScript are
a "near-perfect solution to a very complex, graphically interactive problem",
then why did people come up with front-end frameworks in the first place?

Because they're useful! Frontend developers aren't just cargo culting their
architecture decisions; they're trying to finding that the benefits outweigh
the downsides, particularly with complex apps and large teams.

This article [1] by Tom Dale (one of the authors of Ember) puts it well:

 _> I have heard from many developers who have told me that they accepted the
argument that vanilla JavaScript or microlibraries would let them write
leaner, meaner, faster apps. After a year or two, however, what they found
themselves with was a slower, bigger, less documented and unmaintained in-
house framework with no community. As apps grow, you tend to need the
abstractions that a framework offers. Either you or the community write the
code._

Do all web apps need JavaScript frameworks? Of course not! But let's not
pretend that apps like Trello don't benefit from them.

[1] [https://tomdale.net/2015/11/javascript-frameworks-and-
mobile...](https://tomdale.net/2015/11/javascript-frameworks-and-mobile-
performance/)

~~~
pier25
No single approach covers all use cases, but a real problem is that single-
page apps have been overused.

It makes sense for something like Trello or Gmail, but using the same approach
on every project is a mistake.

