Cycle.js

Dialogue Abstraction

Human-Computer Interaction is a dialogue: an ongoing exchange of messages between the two parts.


    
    function human(senses) {
      // define the behavior of `actuators` somehow
      return actuators; // this may be an observer, event emmitter or similar
    }
    
    function computer(senses) {
      // define the behavior of `outputs` somehow
      return actuators; // this may be an observer, event emmitter or similar
    }
    
    
    var screenEvents = computer(interactionEvents);
    var interactionEvents = human(screenEvents);
    
    // uh oh!
    var screenEvents = computer(human(screenEvents));

Fixed point of a function

In mathematics, a fixed point (sometimes shortened to fixpoint, also known as an invariant point) of a function is an element of the function's domain that is mapped to itself by the function.

 

    
    function human(senses) {
      // define the behavior of `actuators` somehow
      return actuators; // this may be an observer, event emmitter or similar
    }
    
    function computer(senses) {
      // define the behavior of `outputs` somehow
      return actuators; // this may be an observer, event emmitter or similar
    }
    
    var interactionEvents1 = new emptyStream();
    
    // now we can get screenEvents
    var screenEvents = computer(interactionEvents1); 
    
    // with screen events we can get the interactionEvents
    var interactionEvents = human(screenEvents); 
    
    // forward that to the original interaction event stream
    interactionEvents.listen(function(e) {
       interactionEvents1.emit(e); 
    });
    
    /*function human(senses) {
      // define the behavior of `actuators` somehow
      return actuators; // this may be an observer, event emmitter or similar
    }*/
    
    /*function computer(senses) {
      // define the behavior of `outputs` somehow
      return actuators; // this may be an observer, event emmitter or similar
    }*/
    
    // var interactionEvents1 = new emptyStream();
    
    // now we can get screenEvents
    var screenEvents = computer(interactionEvents1); 
    
    // with screen events we can get the interactionEvents
    // var interactionEvents = human(screenEvents); 
    
    // forward that to the original interaction event stream
    /*interactionEvents.listen(function(e) {
       interactionEvents1.emit(e); 
    });*/

Cycle’s core abstraction is your application as a pure function main() where inputs are read effects (sources) from the external world and outputs (sinks) are write effects to affect the external world. 

These side effects in the external world are managed by drivers: plugins that handle DOM effects, HTTP effects, etc.

In short, we want to be able to properly separate logic from effects.

Cycle.js is...

  • Functional and Reactive
  • Simple and Concise
  • Extensible and Testable

Functional and Reactive

 Cycle.js apps are made of pure functions, which means you know they simply take inputs and generate outputs, without performing any side effects.

The building blocks are Observables from RxJSStructuring the application with RxJS also separates concerns, because all dynamic updates to a piece of data are co-located and impossible to change from outside.

As a result, apps in Cycle are entirely this-less and have nothing comparable to imperative calls such as setState() or foo.update().

Simple and Concise

The core API has just one function: run(app, drivers). Besides that, there are Observablesfunctionsdrivers (plugins for different types of side effects), and a helper function to isolate scoped components.

Extensible and Testable

Drivers are plugin-like simple functions that take messages from sinks and call imperative functions. All side effects are contained in drivers. This means your application is just a pure function, and it becomes easy to swap drivers around.

The application is a simple transformation of data.

Explicit data flow

Drivers

Drivers are functions that listen to Observable sinks (their input), perform imperative side effects, and may return Observable sources (their output).

Model View Intent

MVI, the new MVC

  • Intent (listen to the user)
  • Model (to process information)
  • View (output back to the user)

The essential purpose of MVC is to bridge the gap between the human user’s mental model and the digital model that exists in the computer. 
– Trygve Reenskaug, inventor of MVC

Classic MVC

How does MVI compare to MVC?

Model-View-Intent (MVI) is reactivefunctional, and follows the core idea in MVC

It is reactive because Intent observes the User, Model observes the Intent, View observes the Model, and the User observes the View.

It is functional because each of these components is expressed as a referentially transparent function over Observables. 

It follows the original MVC purpose because View and Intent bridge the gap between the user and the digital model, each in one direction.

Show me the CODE!

Cycle.js basic code for a counter

Let's build a clone!

Getting the basics right

First thing we want to do is to actually add the necessary code to create a counter using nothing more than vanilla JS (ES6) and RxJS.

Making it more generic

Experimenting with techniques to remove hard coded dependencies in our toy version.

The infinite loop problem

Using an empty Rx subject to solve the infinite loop problem.

A more generic run function

Separating our run function and making it a little bit more generic by automating proxies and  and subscriptions.

Using cycle.js, part 1

Adding @cycle/core and making our toy DOM driver a little bit more generic.

Using cycle.js, part 2

Using @cycle/core and creating helper functions to address html rendering.

Adding the real DOM driver

Removing the drivers.js file and adding the official @cycle/dom driver

Model View Intent

Refactoring our implementation to use MVI pattern and properly separate concerns.

Or, clone:

Cycle

By Carlos Vega