Skip to content

v8.0.0 - Cycle Nested

Compare
Choose a tag to compare
@staltz staltz released this 21 Dec 19:34
· 223 commits to master since this release

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:

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.