Skip to main content
Cycle.js
Who am I?
Arye Lukashevski
Software Engineer @ Wix.com
Intro
What’s Reactive programming?
Foo depends on Bar
But who owns the relationship?
Foo Bar
Proactive
• Foo invokes the action
• Bar has an API for interaction
• ‘Find usages’
Foo Bar
Proactive Passive
function	onClick()	{	
	Bar.increment();	
}	
Foo
Foo Bar
Listenable Reactive
Foo.addSub('click',	()	=>	{	
	self.increment();	
});	
Bar
Reactive
• Foo emits events
• Bar subscribes
• Bar changes its state
A B
1 0 =A1 + A2
2 0 0
What’s Reactive programming?
A B
1 2 =A1 + A2
2 3 0
What’s Reactive programming?
A B
1 2 5
2 3 0
What’s Reactive programming?
Cycle.JS
Fully Reactive framework
RxJS as stream handlers
DOMDriver
main(DOM){
return fn(DOM);
}
Cycle.js data flow
DOMDriver
main(DOM){
return fn(DOM);
}
Event
Cycle.js data flow
DOMDriver
main(DOM){
return fn(DOM);
}
Event
Cycle.js data flow
DOMDriver
main(DOM){
return fn(DOM);
}
Event
Cycle.js data flow
DOMDriver
main(DOM){
return fn(DOM);
}
Event
Cycle.js data flow
DOMDriver
main(DOM){
return fn(DOM);
}
Event<li/>…<li/>
Cycle.js data flow
DOMDriver
main(DOM){
return fn(DOM);
}
<li/>…<li/>
Cycle.js data flow
DOMDriver
main(DOM){
return fn(DOM);
}
<li/>…<li/>
Cycle.js data flow
DOMDriver
main(DOM){
return fn(DOM);
}
<li/>…<li/>
Cycle.js data flow
DOMDriver
main(DOM){
return fn(DOM);
}
Cycle.js data flow
DOMDriver
main(DOM){
return fn(DOM);
}
Cycle.js data flow
DOMDriver
main(DOM){
return fn(DOM);
}
Cycle.js data flow
Demo - Pizza topping selection
Demo light - Checkbox
let main = ({DOM}) => {

const intent$ = DOM.select('input').events('change');

const model$ = intent$.map(ev => ev.target.checked)

.startWith(false);

const view$ = model$.map(toggled =>

div([

input({type: 'checkbox'}), 'Toggle me',

p(toggled ? 'ON' : 'off')

]));

return {DOM: view$};

};
Cycle.run(main,	{	
	DOM:	makeDOMDriver('#app')	
});
let main = ({DOM}) => {

const intent$ = DOM.select('input').events('change');

const model$ = intent$.map(ev => ev.target.checked)

.startWith(false);

const view$ = model$.map(toggled =>

div([

input({type: 'checkbox'}), 'Toggle me',

p(toggled ? 'ON' : 'off')

]));

return {DOM: view$};

};
Cycle.run(main,	{	
	DOM:	makeDOMDriver('#app')	
});
let main = ({DOM}) => {



















};
Cycle.run(main,	{	
	DOM:	makeDOMDriver('#app')	
});
let main = ({DOM}) => {

const intent$ = DOM.select('input').events('change');

const model$ = intent$.map(ev => ev.target.checked)

.startWith(false);

const view$ = model$.map(toggled =>

div([

input({type: 'checkbox'}), 'Toggle me',

p(toggled ? 'ON' : 'off')

]));

return {DOM: view$};

};
Cycle.run(main,	{	
	DOM:	makeDOMDriver('#app')	
});


div([

input({type: 'checkbox'}), 'Toggle me',

p(toggled ? 'ON' : 'off')

]));

let main = ({DOM}) => {



















};
Cycle.run(main,	{	
	DOM:	makeDOMDriver('#app')	
});
let main = ({DOM}) => {

const intent$ = DOM.select('input').events('change');

const model$ = intent$.map(ev => ev.target.checked)

.startWith(false);

const view$ = model$.map(toggled =>

div([

input({type: 'checkbox'}), 'Toggle me',

p(toggled ? 'ON' : 'off')

]));

return {DOM: view$};

};
Cycle.run(main,	{	
	DOM:	makeDOMDriver('#app')	
});
const intent$ = DOM.select('input').events('change');


div([

input({type: 'checkbox'}), 'Toggle me',

p(toggled ? 'ON' : 'off')

]));

let main = ({DOM}) => {



















};
Cycle.run(main,	{	
	DOM:	makeDOMDriver('#app')	
});
let main = ({DOM}) => {

const intent$ = DOM.select('input').events('change');

const model$ = intent$.map(ev => ev.target.checked)

.startWith(false);

const view$ = model$.map(toggled =>

div([

input({type: 'checkbox'}), 'Toggle me',

p(toggled ? 'ON' : 'off')

]));

return {DOM: view$};

};
Cycle.run(main,	{	
	DOM:	makeDOMDriver('#app')	
});
const intent$ = DOM.select('input').events('change');
const model$ = intent$.map(ev => ev.target.checked)



div([

input({type: 'checkbox'}), 'Toggle me',

p(toggled ? 'ON' : 'off')

]));

let main = ({DOM}) => {



















};
Cycle.run(main,	{	
	DOM:	makeDOMDriver('#app')	
});
let main = ({DOM}) => {

const intent$ = DOM.select('input').events('change');

const model$ = intent$.map(ev => ev.target.checked)

.startWith(false);

const view$ = model$.map(toggled =>

div([

input({type: 'checkbox'}), 'Toggle me',

p(toggled ? 'ON' : 'off')

]));

return {DOM: view$};

};
Cycle.run(main,	{	
	DOM:	makeDOMDriver('#app')	
});
const intent$ = DOM.select('input').events('change');
const model$ = intent$.map(ev => ev.target.checked)



div([

input({type: 'checkbox'}), 'Toggle me',

p(toggled ? 'ON' : 'off')

]));

let main = ({DOM}) => {



















};
Cycle.run(main,	{	
	DOM:	makeDOMDriver('#app')	
});
const view$ = model$.map(toggled =>









return {DOM: view$};
let main = ({DOM}) => {

const intent$ = DOM.select('input').events('change');

const model$ = intent$.map(ev => ev.target.checked)

.startWith(false);

const view$ = model$.map(toggled =>

div([

input({type: 'checkbox'}), 'Toggle me',

p(toggled ? 'ON' : 'off')

]));

return {DOM: view$};

};
Cycle.run(main,	{	
	DOM:	makeDOMDriver('#app')	
});
const intent$ = DOM.select('input').events('change');
const model$ = intent$.map(ev => ev.target.checked)



div([

input({type: 'checkbox'}), 'Toggle me',

p(toggled ? 'ON' : 'off')

]));

let main = ({DOM}) => {



















};
Cycle.run(main,	{	
	DOM:	makeDOMDriver('#app')	
});
.startWith(false);
const view$ = model$.map(toggled =>









return {DOM: view$};
let main = ({DOM}) => {

const intent$ = DOM.select('input').events('change');

const model$ = intent$.map(ev => ev.target.checked)

.startWith(false);

const view$ = model$.map(toggled =>

div([

input({type: 'checkbox'}), 'Toggle me',

p(toggled ? 'ON' : 'off')

]));

return {DOM: view$};

};
Cycle.run(main,	{	
	DOM:	makeDOMDriver('#app')	
});
const intent$ = DOM.select('input').events('change');
const model$ = intent$.map(ev => ev.target.checked)



div([

input({type: 'checkbox'}), 'Toggle me',

p(toggled ? 'ON' : 'off')

]));

let main = ({DOM}) => {



















};
Cycle.run(main,	{	
	DOM:	makeDOMDriver('#app')	
});
.startWith(false);
const view$ = model$.map(toggled =>









return {DOM: view$};
let main = ({DOM}) => {

const intent$ = DOM.select('input').events('change');

const model$ = intent$.map(ev => ev.target.checked)

.startWith(false);

const view$ = model$.map(toggled =>

div([

input({type: 'checkbox'}), 'Toggle me',

p(toggled ? 'ON' : 'off')

]));

return {DOM: view$};

};
Cycle.run(main,	{	
	DOM:	makeDOMDriver('#app')	
});
const intent$ = DOM.select('input').events('change');
const model$ = intent$.map(ev => ev.target.checked)



div([

input({type: 'checkbox'}), 'Toggle me',

p(toggled ? 'ON' : 'off')

]));

let main = ({DOM}) => {



















};
Cycle.run(main,	{	
	DOM:	makeDOMDriver('#app')	
});
.startWith(false);
const view$ = model$.map(toggled =>









return {DOM: view$};
let main = ({DOM}) => {

const intent$ = DOM.select('input').events('change');

const model$ = intent$.map(ev => ev.target.checked)

.startWith(false);

const view$ = model$.map(toggled =>

div([

input({type: 'checkbox'}), 'Toggle me',

p(toggled ? 'ON' : 'off')

]));

return {DOM: view$};

};
Cycle.run(main,	{	
	DOM:	makeDOMDriver('#app')	
});
const intent$ = DOM.select('input').events('change');
const model$ = intent$.map(ev => ev.target.checked)



div([

input({type: 'checkbox'}), 'Toggle me',

p(toggled ? 'ON' : 'off')

]));

let main = ({DOM}) => {



















};
Cycle.run(main,	{	
	DOM:	makeDOMDriver('#app')	
});
.startWith(false);
const view$ = model$.map(toggled =>









return {DOM: view$};
Driver
Intent$
model$
view$
Driver
Intent$
model$
view$
checked:	true,	
target:	input
Driver
Intent$
model$
view$
checked:	true,	
target:	input
Driver
Intent$
model$
view$
checked:	true,	
target:	input
Driver
Intent$
model$
view$
Driver
Intent$
model$
view$
true
Driver
Intent$
model$
view$true
Driver
Intent$
model$
view$
Driver
Intent$
model$
view$<input type=“checkbox” …>
Driver
Intent$
model$
view$
<input type=“checkbox” …>
Driver
Intent$
model$
view$
Driver
Intent$
model$
view$
Component
• Cycle app can be used as a component in a larger Cycle app
const main = ({DOM}) => {

const intent$ = DOM.select('input').events('change');

const model$ = intent$.map(ev => ev.target.checked)

.startWith(false);

const view$ = model$.map(toggled =>

div([

input({type: 'checkbox'}), 'Toggle me',

p(toggled ? 'ON' : 'off')

]));

return {DOM: view$};

};
const main = ({DOM}) => ({DOM:...});
const Toggle = ({DOM}) => ({DOM:...});
const Toggle = ({DOM}) => ({DOM:...});
const main = ({DOM}) => {

























};
const Toggle = ({DOM}) => ({DOM:...});
const main = ({DOM}) => {

const toggleOlives = Toggle({DOM});























};
const Toggle = ({DOM}) => ({DOM:...});
const main = ({DOM}) => {

const toggleOlives = Toggle({DOM});

const toggleMushrooms = Toggle({DOM});




















};
const Toggle = ({DOM}) => ({DOM:...});
const main = ({DOM}) => {

const toggleOlives = Toggle({DOM});

const toggleMushrooms = Toggle({DOM});
const view$ = $.combineLatest(toggleOlives.DOM, toggleMushrooms.DOM,



















};
const Toggle = ({DOM}) => ({DOM:...});
const main = ({DOM}) => {

const toggleOlives = Toggle({DOM});

const toggleMushrooms = Toggle({DOM});
const view$ = $.combineLatest(toggleOlives.DOM, toggleMushrooms.DOM,
(toggleOlives, toggleMushrooms) =>

div([

toggleOlives,

toggleMushrooms

])

);






};
const Toggle = ({DOM}) => ({DOM:...});
const main = ({DOM}) => {

const toggleOlives = Toggle({DOM});

const toggleMushrooms = Toggle({DOM});
const view$ = $.combineLatest(toggleOlives.DOM, toggleMushrooms.DOM,
(toggleOlives, toggleMushrooms) =>

div([

toggleOlives,

toggleMushrooms

])

);
return {

DOM: view$

}

};
DOM.select('input')const Toggle = ({DOM}) => ({DOM:...});
const main = ({DOM}) => {

const toggleOlives = Toggle({DOM});

const toggleMushrooms = Toggle({DOM});
const view$ = $.combineLatest(toggleOlives.DOM, toggleMushrooms.DOM,
(toggleOlives, toggleMushrooms) =>

div([

toggleOlives,

toggleMushrooms

])

);
return {

DOM: view$

}

};
const Toggle = ({DOM}) => ({DOM:...});
const main = ({DOM}) => {

const toggleOlives = Toggle({DOM: DOM.select('.olives')});

const toggleMushrooms = Toggle({DOM});
const view$ = $.combineLatest(toggleOlives.DOM, toggleMushrooms.DOM,
(toggleOlives, toggleMushrooms) =>

div([

toggleOlives,

toggleMushrooms

])

);
return {

DOM: view$

}

};
const Toggle = ({DOM}) => ({DOM:...});
const main = ({DOM}) => {

const toggleOlives = Toggle({DOM: DOM.select('.olives')});

const toggleMushrooms = Toggle({DOM});
const view$ = $.combineLatest(toggleOlives.DOM, toggleMushrooms.DOM,
(toggleOlives, toggleMushrooms) =>

div([

div('.olives', [toggleOlives]),

toggleMushrooms

])

);
return {

DOM: view$

}

};
const Toggle = ({DOM}) => ({DOM:...});
const main = ({DOM}) => {

const toggleOlives = Toggle({DOM: DOM.select(‘.olives')});
const toggleMushrooms = Toggle({DOM: DOM.select('.mushrooms')});

const view$ = $.combineLatest(toggleOlives.DOM, toggleMushrooms.DOM,

(toggleOlives, toggleMushrooms) =>

div([

div('.olives', [toggleOlives]),

div('.mushrooms', [toggleMushrooms])

])

);

return {

DOM: view$

}

};
Component I/O
const props$ = $.of({

name: 'Pineapple'

})
const props$ = $.of({

name: 'Pineapple'

})
const Toggle = ({DOM, }) => {

const intent$ = DOM.select('input').events('change');

const model$ = intent$.map(ev => ev.target.checked).startWith(false);

const view$ = $.combineLatest(model$,

(toggled, ) =>

div([input({type: 'checkbox'}),

,

p(toggled ? 'ON' : 'off')]));

return {

DOM: view$, 



}

};
const props$ = $.of({

name: 'Pineapple'

})
const Toggle = ({DOM, props$}) => {

const intent$ = DOM.select('input').events('change');

const model$ = intent$.map(ev => ev.target.checked).startWith(false);

const view$ = $.combineLatest(model$, 

(toggled, ) =>

div([input({type: 'checkbox'}),

,

p(toggled ? 'ON' : 'off')]));

return {

DOM: view$, 



}

};
const props$ = $.of({

name: 'Pineapple'

})
const Toggle = ({DOM, props$}) => {

const intent$ = DOM.select('input').events('change');

const model$ = intent$.map(ev => ev.target.checked).startWith(false);

const view$ = $.combineLatest(model$, props$,

(toggled, ) =>

div([input({type: 'checkbox'}),

,

p(toggled ? 'ON' : 'off')]));

return {

DOM: view$, 



}

};
const props$ = $.of({

name: 'Pineapple'

})
const Toggle = ({DOM, props$}) => {

const intent$ = DOM.select('input').events('change');

const model$ = intent$.map(ev => ev.target.checked).startWith(false);

const view$ = $.combineLatest(model$, props$,

(toggled, props) =>

div([input({type: 'checkbox'}),

,

p(toggled ? 'ON' : 'off')]));

return {

DOM: view$, 



}

};
const props$ = $.of({

name: 'Pineapple'

})
const Toggle = ({DOM, props$}) => {

const intent$ = DOM.select('input').events('change');

const model$ = intent$.map(ev => ev.target.checked).startWith(false);

const view$ = $.combineLatest(model$, props$,

(toggled, props) =>

div([input({type: 'checkbox'}),

props.name,

p(toggled ? 'ON' : 'off')]));

return {

DOM: view$, 



}

};
const props$ = $.of({

name: 'Pineapple'

})
const Toggle = ({DOM, props$}) => {

const intent$ = DOM.select('input').events('change');

const model$ = intent$.map(ev => ev.target.checked).startWith(false);

const view$ = $.combineLatest(model$, props$,

(toggled, props) =>

div([input({type: 'checkbox'}),

props.name,

p(toggled ? 'ON' : 'off')]));

return {

DOM: view$, 

value$: model$
}

};
const props$ = $.of({

name: 'Pineapple'

})
const Toggle = ({DOM, props$}) => {

const intent$ = DOM.select('input').events('change');

const model$ = intent$.map(ev => ev.target.checked).startWith(false);

const view$ = $.combineLatest(model$, props$,

(toggled, props) =>

div([input({type: 'checkbox'}),

props.name,

p(toggled ? 'ON' : 'off')]));

return {

DOM: view$, 

value$: model$
}

};
const props$ = $.of({

name: 'Pineapple'

})
toggleOlives.value$.map(...)
Next
• More Drivers: HTTP, Storage, React Native
• HTML to hyperscript
• cyc - Generator
• awesome-cyclers: Lots of info
Questions?
Thank you!