v8.0.0 - Cycle Nested
Cycle Nested
Cycle Nested is a new version of Cycle.js with a focus on hard-core reusability: any Cycle.js app can be easily reused in a larger Cycle.js app.
New documentation site at cycle.js.org.
Cycle Nested consists of:
- Cycle Core v6.0.0 or higher
- Cycle HTTP Driver v7.0.0 or higher
- Cycle DOM v8.0.0 or higher
isolate()
, a helper library for components- Other compatible drivers
NEW FEATURES in Cycle Nested:
- Components are simply Cycle.js apps (
main()
renamed to e.g.Button()
) that can be reused in larger apps. - Cycle DOM introduces hyperscript helpers, so you can create virtual DOM with functions like
div()
,h1()
,ul()
,li()
,span()
, etc.
Migration guide
Names changed.
Before | After |
---|---|
DOM.get() |
DOM.select().events() |
Response (naming convention) | Source (naming convention) |
Request (naming convention) | Sink (naming convention) |
Cycle DOM mockDOMResponse() |
Cycle DOM mockDOMSource() |
labeledSlider (custom element) |
LabeledSlider (dataflow component) |
let [sinks, sources] = Cycle.run(m, d) |
let {sinks, sources} = Cycle.run(m, d) |
Custom Elements removed. Dataflow components replace them.
-function labeledSlider(sources) {
+function LabeledSlider(sources) {
- const initialValue$ = sources.props.get('initial')
+ const initialValue$ = sources.props$
+ .map(props => props.initial)
.first();
const newValue$ = sources.DOM
.select('.slider')
.events('input')
.map(ev => ev.target.value);
const value$ = initialValue$.concat(newValue$);
- const props$ = sources.props.getAll();
const vtree$ = Rx.Observable
- .combineLatest(props$, value$, (props, value) =>
+ .combineLatest(sources.props$, value$, (props, value) =>
h('div.labeled-slider', [
h('span.label', [
props.label + ' ' + value + props.unit
]),
h('input.slider', {
type: 'range',
min: props.min,
max: props.max,
value: value
})
])
);
return {
DOM: vtree$,
- events: {
- newValue: newValue$
- }
+ value$: value$
};
}
The function above, LabeledSlider()
follows the same techniques we use to build any main()
function in Cycle.js. There is no magic and no tricks, it is simply a function that does what it says it does. The usage of components is very different to the usage of custom elements.
Not necessary to register the component anymore:
-const domDriver = CycleDOM.makeDOMDriver('#app', {
- 'labeled-slider': labeledSlider // our function
-});
+const domDriver = CycleDOM.makeDOMDriver('#app');
Using a component in a parent view has changed:
function main(sources) {
// ...
- const childValue$ = state(intent(sources.DOM));
+ const props$ = Observable.of({
+ label: 'Radius', unit: '', min: 10, initial: 30, max: 100
+ });
+ const childSources = {DOM: sources.DOM, props$};
+ const labeledSlider = LabeledSlider(childSources);
+ const childVTree$ = labeledSlider.DOM;
+ const childValue$ = labeledSlider.value$;
- const vtree$ = childValue$.map(
- value =>
+ const vtree$ = childVTree$.withLatestFrom(childValue$,
+ (childVTree, value) =>
h('div', [
- h('labeled-slider#weight', {
- key: 1, label: 'Weight', unit: 'kg',
- min: 40, initial: weight, max: 140
- }),
+ childVTree,
div({style: {
backgroundColor: '#58D3D8',
width: String(value) + 'px',
height: String(value) + 'px',
borderRadius: String(value * 0.5) + 'px'
}})
])
);
return {
DOM: vtree$
};
}
To get events from a component, we don't use anymore DOM.select('labeled-slider').events('myCustomEvent')
. Instead, we just get the Observable returned from the LabeledSlider
component function.
Read more instructions about Dataflow components.
Using hyperscript helpers:
+import { div, span, input } from '@cycle/dom';
-h('div.labeled-slider', [
+div('.labeled-slider', [
- h('span.label', [
+ span('.label', [
props.label + ' ' + value + props.unit
]),
- h('input.slider', {
+ input('.slider', {
type: 'range',
min: props.min,
max: props.max,
value: value
})
])
For more help, read the new documentation site Cycle.js.org or ask for help in the Gitter chat room.