cycle.js - functional reactive ui framework (nikos kalogridis)
TRANSCRIPT
WHY ANOTHER FRAMEWORK?Programming is hard!Complexity of Web apps is growing exponentiallyAsynchronous programming complicates thingseven more
CYCLE.JS FEATURESFunctionalReactiveUnidirectonal flow of dataVirtual DOMA Cycle.js program is a pure function - no sideeffects
CYCLE.JS HISTORYNov-2014 - Initial commit on github based on RxJsNov-2015 - A fork of the project was made basedmost.jsJun-2016 - Merged back this time supportingxstream, Rxjs v4, Rxjs v5 and most.jsTC39 Observable proposalFeb-2017 - Dropped stream adapters and now usesObservable to convert between streams. Alsodropped support for Rxjs v4
MAP
Returns a new array with the result of applying afunction on each element of the array
var myArray = [1, 2, 3, 4, 5];
myArray.map(function (value) { return value + 1; });
// -> [2, 3, 4, 5, 6]
FILTER
Returns a new array with all elements that pass thetest implemented by the provided function
var myArray = [1, 2, 3, 4, 5];
myArray.filter(function (value) { return value < 4; });
// -> [1, 2, 3]
REDUCE
Reduces the elements to a single value applying afunction to each of them
var myArray = [1, 2, 3, 4, 5];
myArray.reduce(function (acc, value) { return acc + value; }, 0);
// -> 15
IMPERATIVE VS DECLARATIVEGiven an array of numbers return the numbers that
are less than 4 and add to each of those 10var myArray = [1, 2, 3, 4, 5]; var result = []; var i = 0;
for (i; i < myArray.length; i += 1) { if (myArray[i] < 4) { result.push(myArray[i] + 10); } }
var myArray = [1, 2, 3, 4, 5]; var result = myArray .filter((value) => value < 4) .map((value) => value + 10);
HIGHER ORDER FUNCTIONEXAMPLES
function add(amount) { return function (value) { return value + amount; } }
function lessThan(amount) { return function (value) { return value < amount; } }
var result = myArray.filter(lessThan(4)).map(add(10));
SIDE EFFECTSvar a = 0;
function impure(x) { a = x + a; }
function impure(x) { console.log(x); return x + 1; }
CALLBACKSvar counter = 0;
function callback() { counter += 1;}
document .getElementById('myButton') .addEventListener('click', callback);
var xhr = new XMLHttpRequest(); xhr.open('GET', '/server', true);
xhr.onload = function () { // Request finished. Do processing here. };
PROMISESvar requestPromise = fetch({url: 'http://www.google.com'});
requestPromise .then(function (response) { // do something with the response }) .catch(function (error) { // handle the error });
GENERATORS / YIELDfunction* foo () { var index = 0; while (index < 2) { yield index++; } } var bar = foo();
console.log(bar.next()); // { value: 0, done: false } console.log(bar.next()); // { value: 1, done: false } console.log(bar.next()); // { value: undefined, done: true }
ASYNC / AWAITasync function save(Something) { try { await Something.save(); } catch (ex) { //error handling } console.log('success');}
OBSERVABLE// data$ is an Observable object var subscription = data$.subscribe( { next: function (value) { // handle next value }, error: function (error) { // handle error }, complete: function () { // finished so do cleanup... } } );
REACTIVE PROGRAMMINGis programming with data streams (synchronous orasynchronous)on top of that you are given an amazing toolbox offunctions to combine, create, filter any of thosestreams
RXJS EXAMPLEimport Rx from 'rxjs/Rx';
let counter = 0;
const increaseButton = document.querySelector('#increase'); const increaseClick$ = Rx.Observable.fromEvent(increaseButton, 'click');
increaseClick$.subscribe({ next: function () { // click received counter += 1; console.log(counter); } });
RXJS EXAMPLE REVISEDimport Rx from 'rxjs/Rx'; const increaseButton = document.querySelector('#increase'); const increaseClick$ = Rx.Observable.fromEvent(increaseButton, 'click'); const counter$ = increaseClick$ .mapTo(1) // always maps to a constant value .scan((acc, value) => acc + value, 0); // hint: reduce
counter$.subscribe({ next: function (counter) { // new count event received console.log(counter); } });
OTHER TYPES OF STREAMSRx.Observable.from([1, 2, 3, 4, 5]) .filter((x) => x < 4) .map((x) => x + 10) .subscribe({ next: (value) => console.log(value), complete: () => console.log('done') });
// ->// 11// 12// 13// done
PROMISE AS A STREAMconst myPromise = new Promise((resolve) => resolve('hello')); Rx.Observable.from(myPromise) .subscribe({ next: (value) => console.log(value), complete: () => console.log('done') });
// ->// hello // done
COMBINING STREAMSconst data1$ = Rx.Observable.from([1, 2, 3]);const data2$ = Rx.Observable.from([6, 7, 8]);Rx.merge(data1$, data2$) .subscribe({ next: (value) => console.log(value) });// ->// 1 // 6 // 2 // 7 // 3 // 8
function main(sources) { const model$ = sources .DOM.select('.increase').events('click') .mapTo(1).fold((acc, value) => acc + value, 0);
const vtree$ = model$.map((value) => div([ button('.increase', 'Increase'), span(value) ]) );
return {DOM: vtree$}; }
Cycle.run(main, { DOM: makeDOMDriver('#app') });
NO SIDE EFFECTS
SourcesSinks
main()
DOM side effects
HTTP side effects
Other side effects
pure dataflow
PROSTruly reactive programming styleDeclarative UI designFast - thanks to Snabdom and xstreamFractal state management
CONSBrain rewiring for using functional reactive styleRelatively small community compared to React orAngularSteep learning curve especially on stream librariesLacks a complete UI component library (as of today)
USEFUL LINKShttps://cycle.js.org/http://widdersh.in/tricycle/https://github.com/cyclejs-communityhttps://gitter.im/cyclejs/cyclejshttps://github.com/staltz/cycle-onionifyhttps://github.com/cyclejs-community/cyclic-routerhttp://staltz.com