Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Getting Reactive with Cycle.js and xstream

346 views

Published on

* What is Reactive Programming with Observable Streams and Cycle.js.
* Why I should I care?
* How can I get started? 

Published in: Software
  • Be the first to comment

  • Be the first to like this

Getting Reactive with Cycle.js and xstream

  1. 1. Getting Reactive with Cycle.js and XStream TechExeter2017 Steve Lee steve@opendirective.com @SteveALee
  2. 2. Three things • What is Reactive Programming with Streams and Cycle.js. • Why I should I care? • How can I get started?
  3. 3. What we’ll cover • Reactive programming with streams • Cycle.js • A little Functional Programming • Some ES6+ Javascript syntax • Virtual DOM and/or JSX
  4. 4. What is Reactive Programming? • Reactive programming is programming with asynchronous data streams – Andre Staltz
  5. 5. What is a Stream?
  6. 6. Observable and Observer Observable Observer
  7. 7. Nothing New • Event busses, queues, handlers… • Microsoft Rx Extensions about 5 yrs old • Netflix use Rx for their apps • Angular provides Rx as an option
  8. 8. Let’s Start from the Top
  9. 9. What is Cycle.js • Tiny amount of code • Small set of general principles • “A unified theory of everything, but for JavaScript.” - @Widdershin • It’s streams all the way down
  10. 10. If in doubt…
  11. 11. Principles • Event loop between App and external world • I/O Side effects separate from application logic • No global state – data flows & reducers
  12. 12. Composable
  13. 13. Bonuses • Explicit asynchronous dataflow • Extensible by adding new drivers • Relatively easy to test
  14. 14. Show me some Code! https://jsbin.com/tewiwal/edit?js,output
  15. 15. HTML // Placeholder element <body> <div id="app"></div> </body>
  16. 16. Import Dependencies // ES6 imports via Babel or TypeScript // or global script with NPM CDN const xs = … xstream const Cycle = … cycle.js const makeDOMDriver = … @cycle/DOM
  17. 17. Cycle.js Entry Function // The Cycle.js framework API! Cycle.run(Toggle, { DOM: makeDOMDriver('#app') });
  18. 18. Main Componet // Toggle is our main component - App Cycle.run(Toggle, { DOM: makeDOMDriver('#app') });
  19. 19. Collection of Drivers Cycle.run(Toggle, { DOM: makeDOMDriver('#app') });
  20. 20. Drivers • Just functions • Output one of component sources – DOM event Stream selector • Input is a sink stream from the component – VDOM updates – becomes the DOM • Cycle connects based on Driver name
  21. 21. v DOM Events VDOM Updates
  22. 22. A Cycle.js Component function Toggle(sources) { const sinks = { DOM: …}; return sinks; }
  23. 23. A Cycle.js Component // Passed sources, one per driver // Returns Sinks, one per useful output stream function Toggle((sources) { const sinks = { DOM: /* VDOM update stream */ }; return sinks; }
  24. 24. The Complete Component function Toggle(sources) { const sinks = { DOM: sources.DOM .select('input').events('change') .map(ev => ev.target.checked) .startWith(false) .map(toggled => div([ input({attrs: {type: 'checkbox'}}), 'Toggle me', p(`${toggled ? 'ON' : 'off'}`) ]) ) }; return sinks; }
  25. 25. Model View Intent
  26. 26. Component with MVI function Toggle({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({attrs: {type: 'checkbox'}}), 'Toggle me', p(`${toggled ? 'ON' : 'off'}`) ]) ) sinks = { DOM: view$ } return sinks }
  27. 27. Object Destructuring Given const sources = { DOM: … } Then const DOM = sources.DOM Can be written as const {DOM} = sources foo({DOM})
  28. 28. User Intent function Toggle({DOM}) { const intent$ = DOM.select('input').events('change’) … return sinks }
  29. 29. DOM Driver events • All input events come in through sources • Driver lets you use CSS selectors • Composition requires event “isolation”
  30. 30. Model // state is true or false, initially false function Toggle({DOM}) { … const model$ = intent$.map(ev => ev.target.checked) .startWith(false) … return sinks }
  31. 31. ES6 Fat Arrow Functions const mapper = ev => ev.target.checked Somewhat equivalent to Function mapper(ev){ return ev.target.checked } • Expression • Shortcut syntax • No “wtf is this” problems
  32. 32. View // Return a stream of VDOM Nodes function Toggle({DOM}) { … const view$ = model$.map(toggled => div([ input({attrs: {type: 'checkbox'}}), 'Toggle me', p(`${toggled ? 'ON' : 'off'}`) ]) ) sinks = { DOM: view$ } return sinks }
  33. 33. Virtual DOM • DOM updates are slow • Virtual DOM looks like DOM but is faster • Manages when to update DOM and what • Practical to define view based on state • React made Virtual DOMs popular • Cycle uses one called snabbdom
  34. 34. Snabbdom // Hyperscript helper functions. Can also use JSX div([ input({attrs: {type: 'checkbox'}}), 'Toggle me', p(`${toggled ? 'ON' : 'off'}`) ]) Is rendered as this HTML <div> <input type="checkbox">Toggle me <p>off</p> </input> </div>
  35. 35. ES6 Template string // backtick allows embedded expressions const string = `${toggled ? 'ON' : 'off'}`
  36. 36. Stream view // Select a stream of DOM events sources.DOM.select('input').events('change’) // Converts each event to checked state .map(ev => ev.target.checked) // Initial state is false .startWith(false) // Convert state to VDOM update .map(toggled => div([ input({attrs: {type: 'checkbox'}}), 'Toggle me', p(`${toggled ? 'ON' : 'off'}`) ]) )
  37. 37. Streams • Explicit flow of ‘events’ • From Sources (producer) to sinks (consumer) • Through a sequence of simple operators
  38. 38. Declarative • The stream operators say what happens • Not how – eg no imperative loops • Uses functional style operators on streams – Eg map, reduce (aka fold), filter …. – Similar to array extras, but over time not in memory – Stream specific operators like “merge”
  39. 39. Marble Diagram http://rxmarbles.com/
  40. 40. Immutable • Operators return new streams • Pure functions – no surprises • No modification of global state
  41. 41. State handling • Follow events • Fold() modifies ‘state’ over a series of events • Onionify provide single state tree cf. Redux – Layered to match composition structure – State input to component as a Stream – Changes in state returned as reducer functions – Persist to browser storage • Roll your own
  42. 42. Summary • Component is functional stream code – no side effects, immutable, pure • Side effects in the drivers • Intent selected events from the DOM driver • Intent maps to model state • Model maps to view stream of VNodes • VNodes returned to driver for rendering
  43. 43. v DOM Events VDOM Updates
  44. 44. What about those Observables? • Cycle keeps them in drivers • Interface between streams and side effects • Keeps ‘external’ imperative logic out of main • Uses xstream library rather then RxJS – Always hot
  45. 45. Observable pattern • Separates producer and consumer • Streams between observable and observer • Pub Sub • Push • GoF “Observer” plus “Iterable” patterns • ECMAScript TC39 Stage 1 proposal
  46. 46. // Adds a listener (Observer) to the input stream Function logDriver(msg$){ msg$.addListener({ next: msg => console.log(msg) }) /* no source */ } A Write-only Driver
  47. 47. // Pushes message to stream when a websocket message arrives function WSDriver(/* no sinks */) { return xs.create({ start: listener => { this.connection = new WebSocket('ws://localhost:4000’); connection.onmessage = (msg) => { listener.next(msg) } }); } A Read-only Driver
  48. 48. Learning Curve - Brain rewire • Seems weird if only used to Classical OOP • New idioms and traps to learn • Different method of state handling • Much easier than just using streams • ES6+ Fat Arrows, Destructuring, Rest/Spread
  49. 49. Why bother • Explicitly handles async complexity – Simpler than long promise chains • Cleaner declarative code • Functional code predicability • Pure code is easier to test - no mocking • You might like it • Matches “Event driven” serverless
  50. 50. Why Not? • Effort to internalise new concepts • Is young and still being refined • Debugging can be tricky as lack of tools – Often effectively log to console • Finding developers may be hard – FP is sadly not as familiar as it could be • Community is small – also good
  51. 51. How can I start? • CreateCycleApp and One Fits All flavor • Cycle.js docs and samples • Staltz’s “The introduction to Reactive Programming you've been missing” • Staltz’s egghead.io courses • Helpful community on GitHub and gitter
  52. 52. Questions? @SteveALee steve@opendirective.com

×