3 ms·
[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 v
by lilactown 8y ago
[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.
- lilactown 8y agoAnother difference I feel is worth mentioning: events in Cycle.js are "pull"-based. Look at how you can select an element in Cycle's DOM tree and subscribe to events to it as a stream. Events in re-frame (and reagent and React.js in general) are "push"-based. You must be explicit in your UI tree which events we care about and when they are emitted. I actually wrote a library a long time ago[0] to invert this in React and expose a more pull-based event API, but in practice it wasn't very ergonomic. [0] https://github.com/Lokeh/observe-component https://github.com/Lokeh/observe-component
- vemv 8y agoThat'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 8y agoThe 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. ;)