Upgrade to Pro — share decks privately, control downloads, hide ads and more …

React, Transparent Reactive Programming and Mutable data structures

React, Transparent Reactive Programming and Mutable data structures

The ability to express essential complexity in a simple way is crucial for any code-base. At Mendix we did an interesting discovery during the development of a complex MDD tool. React, mutable data structures and transparent reactive programming are a match made in heaven. We published a library that leverages these concepts; MobX. It helps you to write simple, declarative, yet highly efficient code. Your future code maintainers will love you for applying it.

Michel Weststrate

November 04, 2015
Tweet

More Decks by Michel Weststrate

Other Decks in Programming

Transcript

  1. DON'T BUILD APPS. BUILD SPREADSHEETS. Reactive programming and mutable data

    structures in large applications Michel Weststrate @mweststrate
  2. ❏ React contexts ❏ Store / data subscriptions ❏ Lenses

    ❏ Smart components ❏ Stateful components ❏ Immutable State ❏ Denormalized data
  3. Data Cells Formulas, Charts Data Entry / Import mutates formula

    (observe) Library Stars React 30,588 Angular 43,808 MobX 1,418
  4. const store = { boxes: [], arrows: [], selection: null

    }; store.boxes.push( new Box("Rotterdam", 100, 100), new Box("Vienna", 700, 150) ); store.arrows.push({ id: randomUuid(), from: store.boxes[ 0], to: store.boxes[ 1] }); classes plain objects arrays references boxes arrows
  5. const store = observable({ boxes: [], arrows: [], selection: null

    }); store.boxes.push( new Box("Rotterdam", 100, 100), new Box("Vienna", 700, 150) ); store.arrows.push({ id: randomUuid(), from: store.boxes[ 0], to: store.boxes[ 1] }); boxes arrows make stuff recursively observable
  6. class Box { id = randomUuid(); @observable name = "A

    box"; @observable x = 0; @observable y = 0; @observable get width() { return this.name.length * 15; } constructor(name, x, y, id) { this.name = name; this.x = x; this.y = y; this.id = id || randomUuid(); } }
  7. Immutables Data Structures ❏ Never change ❏ Structural equality ❏

    Easy to memoize, structural sharing, no defensive copies, time travelling ❏ Assumes state is a tree, not a graph ❏ Denormalize the rest boxes arrows
  8. Observable Mutable Data Structures ❏ Identity equality ❏ Concept should

    exists only once in memory ❏ Always up-to-date ❏ Composition & associations ❏ Prototypes and type checking ❏ Natural mental model ❏ Simple actions boxes arrows
  9. class Canvas extends Component { render() { const {store} =

    this.props; return ( <div className="app"> <div className="canvas"> <svg> { store.arrows.map(arrow => <ArrowView arrow={arrow} key={arrow.id} /> ) } </svg> { store.boxes.map(box => <BoxView box={box} store={store} key={box.id} /> ) } </div> <Sidebar store={store} /> </div> ); } } arrows boxes
  10. class ArrowView extends Component { render() { const {from, to}

    = this.props.arrow; const [x1, y1, x2, y2] = [ from.x + from.width / 2, from.y + 30, to.x + to.width / 2, to.y + 30 ]; return <path className="arrow" d={`M${x1} ${y1} L${x2} ${y2}`} />; } } @observer boxes box props ?! that's all
  11. @observer class Canvas extends Component { render() { const {store}

    = this.props; return ( <div className="app"> <div className="canvas"> <svg> { store.arrows.map(arrow => <ArrowView arrow={arrow} key={arrow.id} /> ) } </svg> { store.boxes.map(box => <BoxView box={box} store={store} key={box.id} /> ) } </div> <Sidebar store={store} /> </div> ); } }
  12. @observer ❏ mobx.autorun(() => this.render()) ❏ autorun ❏ Takes a

    function, makes it reactive: ❏ After each run: subscribe to all data accessed while running ❏ Re-run on data changes ❏ Optimizes dependency tree ❏ PureRenderMixin
  13. const ArrowView = observer(props => { const {from, to} =

    props.arrow; const [x1, y1, x2, y2] = [ from.x + from.width / 2, from.y + 62, to.x + to.width / 2, to.y ]; return <path className="arrow" d={`M${x1} ${y1} L${x2} ${y2}`} />; }); stateless function components
  14. @observer class Sidebar extends Component { render() { const {selection}

    = this.props.store; return selection !== null ? < div className="sidebar sidebar-open" > < input onChange={this.onChange} value={selection.name} /> </ div> : < div className="sidebar" />; } onChange = (e) => { } } onChange = (e) => { this.props.store.selection.name = e.target.value; }; } just references updates all 'name' observers
  15. onCanvasMouseUp = (e) => { const {store} = this.props; if

    (e.button === LEFT_BUTTON) { store.selection = null; } else if (e.button === RIGHT_BUTTON) { const newBox = store.addBox( "Hi.", e.clientX - 50, e.clientY - 20, store.selection ); store.selection = newBox; } }
  16. store.addBox = function(name, x, y, fromBox ) { const newBox

    = new Box(name, x, y); this.boxes.push(newBox); if (fromBox) { this.arrows.push({ id : randomUuid(), from: fromBox, to: newBox }); } return newBox; };
  17. MobX core API (@)observable Makes stuff observable autorun Creates a

    self-updating function that monitors observable data @observer Wraps react component render in autorun
  18. Transparent reactive programming 1. Decorate all objects properties and arrays

    indices 2. Store running views in a stack 3. Getters register observers 4. Setters notify observers autorun Function Stack x * 2 get x x x set x
  19. Dependency tree analysis 1. Subscription change over time! 2. Synchronous

    updates + transactions 3. Atomic updates 4. Cycle detection 5. Non-pure view function detection 6. Lazy / eager evaluation 7. Garbage collection
  20. The most fluid parts of your application should be the

    simplest to build github.com/mobxjs/mobx feel free to more ask on gitter! in production
  21. @observer class BoxView extends Component { render() { const {box}

    = this.props; return (<DraggableCore onDrag={this.handleDrag}> <div style={{ width: box.width, left: box.x, top: box.y }}> {box.name} </div> </DraggableCore>); } handleDrag = (e, dragInfo) => { this.props.box.x += dragInfo.position.deltaX; this.props.box.y += dragInfo.position.deltaY; } }
  22. Questions? For example: 1. What about garbage collection? 2. Does

    it evaluate eagerly or lazily? 3. What about async actions? 4. Why not RxJS? 5. It updates synchronously, isn't that expensive? 6. What about flux?