React, life without MVC

Priyatam Mudivarti

Principal, Facjure

ForwardJs 3

JULY 29TH, SAN FRANCISCO

Trygve Reenskaug 
introduced MVC into Smalltalk-76 while visiting 
Xerox Parc in 1979.

Our industry has chosen code written in terms of classes. A class tells us everything about the properties of the individual objects that are its instances. It does not tell us anything about how these instances work together to achieve the system behavior.

30+ years of MVC

MVC was the first to describe and implement software constructs in terms of their responsibilities.

 

MVC used a set of protocols to define components instead of using concrete implementations.

 

However, many web developers find it hard to create a model up front. The system structure evolves as it is being shaped directly in code.

What's wrong with MVC?

Nothing!

but it can be limiting

MVC WAS ARCHITECTED FOR
ANOTHER AGE

realtime feeds, static lists, timed—batch sync, search ...

REACt

in a few lines of jQuery!

source: http://hackflow.com/blog/2015/03/08/boiling-react-down-to-few-lines-in-jquery/

Demo (simple)

two inputs, one output 

<span id="colored-counter">0</span>
<input id="color"></input>
<button id="inc"></button>


$('#color').on('keyup', function () {
    $('#colored-counter').css('color', this.value);
})

$('#inc').on('click', function () {
    var oldValue = $('#colored-counter').html();
    var newValue = 1 + Number(oldValue);
    $('#colored-counter').html(newValue);
})

Demo

multiple inputs + outputs, undo 

can't scale

M*N (events, elements)

Refactor #1

introducE state

/*
  <span id="colored-counter">0</span>
  <input id="color"></input>
  <button id="inc"></button>
*/

var state = {color: '', value: 0};

function updateUI() {
    $('#colored-counter').css('color', state.color);
    $('#colored-counter').html(state.value);
}

$('#color').on('keyup', function () {
    state.color = this.value;
    updateUI();
})

$('#inc').on('click', function () {
    state.value++;
    updateUI();
})

Refactor #2

serialize state

function updateUI() {
    // Save latest state to local storage
    LocalStorage.set('state', JSON.stringify(state));
    // ...
}

// Load saved state from local storage on page load
$(function () {
    state = JSON.parse(LocalStorage.get('state'));
    updateUI();
});

Refactor #3

state = data structures

 Instead of copying and mutating state, build new state 
based on previous state 

  +---+    +---+    +---+    +---+
  | 4 +--->+ 3 +--->+ 2 +--->+ 1 |
  +---+    +---+    +---+    +---+
    |        |
newList  original
source: http://www.jayway.com/2013/03/03/git-is-a-purely-functional-data-structure/
              +---+      +---+    +---+    +---+
new list 1 -> | 4 +---+->+ 3 +--->+ 2 +--->+ 1 |
              +---+  /   +---+    +---+    +---+
                    /      |
              +---+/    original
new list 2 -> | 9 +
              +---+
source: http://www.jayway.com/2013/03/03/git-is-a-purely-functional-data-structure/
                +---+    +---+
updated list -> | 4 +--->+ 5 +----+
                +---+    +---+     
                     
                +---+    +---+    +-+-+    +---+
  new list 1 -> | 4 +--->+ 3 +--->+ 2 +--->+ 1 |
                +---+  / +---+    +---+    +---+
                      /    |
                +---+/  original
  new list 2 -> | 9 +
                +---+
source: http://www.jayway.com/2013/03/03/git-is-a-purely-functional-data-structure/

 

 

preserve state on page reloads

serialize history to backend

reproduce errors users face 

go back in time

/*
  <span id="time-pos"></span>
  <button id="back">Back</button>
  <button id="next">Next</button>  
*/

var time = {history: [], pos: 0};

function updateTimeUI() {
    $('#time-pos').html('Position ' + time.pos + ' of ' + time.history.length);
}

function saveState() {
    time.history.push(deepcopy(state));
    time.pos++;
    updateTimeUI();
}

$('#back').on('click', function () {
    time.pos--;  // Move history pointer
    updateTimeUI();
    state = deepcopy(time.history[time.pos]); // Load historic state
    updateUI();
})

$('#next').on('click', function () {
    time.pos++;
    // ...
})

function updateUI() {
    saveState();
    // render compontents
}

Refactor #4

Optimize rendering

 render(appstate) => HTML 

function render(state) {
    var span = '<span id="count">' + state.items.length + '</span>';
    var lis = state.items.map(function (item) {
        return '<li>' + item + '</li>';
    });
    return span + '<ul>' + lis.join('') + '</ul>'
}

function updateUI() {
    $('#ui').html(render(state));
}

Refactor #5

save memory:
persistent data-structures

instead of N, create logN new objects and reuse: faster diffs.

source: http://jr0cket.co.uk/slides/functional-advantage-with-clojure.html
var object = {
    a: {x: 1, y: 2},
    b: {text: 'hi'}
}

// Now instead of object.a.y = 3 we do
var object2 = {
    a: {x: object.a.x, y: 3},
    b: object.b
}



// Suppose you have an object with many keys and a value of one of them changes
var prev = ['a', 'b', 'c', ..., 'z'];
var next = prev.slice(0, n-1).concat([newValue]).concat(prev.slice(n));
// slow!
var prev = {
    '0-3': {
        '0-1': {0: 'a', 1: 'b'},
        '2-3': {...},
    },
    '4-7': {...}
}
var next = {
    '0-3': {
        '0-1': prev['0-3']['0-1'],
        '2-3': {
            2: 'hey',
            3: prev['0-3']['2-3'][3]
        }
    },
    '4-7': prev['4-7']
}
// Turns out many immutable/persistent ds implementations exist
// Facebook also has one: ImmutableJs

var object = Immutable.fromJS({
    a: {x: 1, y: 2},
    b: {text: 'hi'}
})

var object2 = object.setIn(['a', 'y'], 3); // object remains intact

Refactor #5

minimize dom mutations

 abstract/virtual dom

var root = document.getElementById('ui');
var prevState = state, prevTree = [];

function render(state) {
    // Virtual DOM is really just a tree of JavaScript objects or arrays
    return [
        ['span', {id: 'count'}, state.items.length],
        ['ul', {}, state.items.map(function (item) {
            return  ['li', {}, item]
        })]
    ]
}

function updateUI() {
    var vTree = render(state);
    var diff = vDiff(prevTree, vTree); // Just a diff on data structures :)
    vApply(root, diff)                 // Apply series of patches to real DOM

    prevState = deepcopy(state);
    prevTree = vTree;
}

who has time to optimize this

while (true)
{
  processInput();
  update();
  render();
}
var ToggleText = React.createClass({
  getInitialState: function () {
    return {
      showDefault: true
    }
  },
  
  toggle: function (e) {
    // Prevent following the link.
    e.preventDefault();
    
    // Invert the chosen default.
    // This will trigger an intelligent re-render of the component.
    this.setState({ showDefault: !this.state.showDefault })
  },
  
  render: function () {
    // Default to the default message.
    var message = this.props.default;
    
    // If toggled, show the alternate message.
    if (!this.state.showDefault) {
      message = this.props.alt;
    }
    
    return (
      <div>
        <h1>Hello {message}!</h1>
        <a href="" onClick={this.toggle}>Toggle</a>
      </div>
    );
  }
});
import connectToStores from 'alt/utils/connectToStores';
import TodoStore from './TodoStore';
import { Component } from 'react';

@connectToStores
class TodoView extends Component {
  static getStores() {
    return [TodoStore];
  }

  static getPropsFromStores() {
    return TodoStore.getState();
  }

  render() {
    return (
      <ul>
        {this.props.todos.map((todo) => {
          return (
            <li key={todo.id}>{todo.text}</li>
          );
        })}
      </ul>
    );
  }
}

REACT focusES on two things:

 

Rendering the UI, from STATE

Responding to user inputs (events)

source: "Immutable Front-end in ClojureScript" by Logan Linn

write UI once, render a dynamic, data-driven (state) page that works for both initial creation and future updates—ideally, with no dom manipulation

Render

from scratch, every time

optionally, batch every requestAnimationFrame

How to render efficient Dom updates? 

 

Instead of attempt­ing to mutate a sta­tic DOM in—place, React effec­tively regen­er­ates the DOM from scratch with every update to the appli­ca­tion data. 

Virtual Dom


Oper­at­ing on the VDOM is quicker than oper­at­ing on the browser’s native DOM 


Gen­er­ate HTML from plain Javascript data

Virtual Dom is opaque

state management

OR, manging state over time

(not that simple)

Who owns the state

Where does it flow

How does it change

Understanding your UI components
=

Understanding their life cycle

state

Identity and state are not equivalent things. 

Object Oriented programming typically unifies identity and state.

this includes javascript

source: http://www.raywenderlich.com/109030/rwdevcon-inspiration-talk-identity-by-alexis-gallagher

An identity is an entity that has a state, which is its value at a point in time

state           ui

‘Is this checkbox checked’

 

‘Did my ajax call succeed, and was I able to decode the JSON response and if yes, is the status field OK?’
 

The user usually clicks around, types things, gets angry at his wife and submits the form ten times and finds breakable parts of our UI that we never ever dreamed of.

UI IS A SNAPSHOT OF STATE, IN TIME

UI is build with components

that can share state ... over time

they can communicate!

The bigger your interface gets, the more likely it is to have one interaction that triggers an update, which in turn triggers another update, which in turn trigger many other components. 

 

React delegates all events in the application to a single event handler (Dispatcher)

 

Batch strategies allow you to render at requestAnimationFrame

 

Top-down, value-based rendering allow component composition
 

Uni-directional flow

Components          Events

model
STATE "AS" Data

views
components "AS" functions

controleRS 
Communicating EVENTS 

Instead of cascading changes on Dom => batch changes on Virtual Dom

 

Instead of inserting Js in  => HTML Put Html in Js

 

Instead of communicating from child view to parent => communicate from Parent to Child views
 

Instead of a separate Markup, Code, Styles => Put them in one place

Simpler primitives
MORE experimentation

Good frameworks leverage the strengths of the language, not attempt to abstract them away.

 

Clojurescript^REACT

or how I stopPED worrying about react 

Clojurescript fixes common patterns and issues
in React at the language level.

 

Clojurescript models state, identity, time, and ASYNC

in the language

=> State management and communication primitives, built-in

more Efficient diffs

overrides shouldComponentUpdate to a cheap identity check: cljs data-structures have persistent, immutable.

batched updates

on requestAnimationFrame

React doesn't currently use requestAnimationFrame to do DOM updates (as we call it, the "batching strategy"). The batching strategy is injectible though so it's possible to use something else. Om makes use of that possibility and uses requestAnimationFrame to batch DOM updates 

- Facebook team

(def ^:private refresh-queued (atom #{}))

(def ^:private req-anim-frame
  (if (exists? js/requestAnimationFrame)
    js/requestAnimationFrame
    (fn [f] (js/setTimeout f 16))))

(defn mount [element node]
  (when-not (@refresh-queued node)
    (swap! refresh-queued conj node)
    (req-anim-frame (fn []
                      (swap! refresh-queued disj node)
                      (js/React.render element node)))))
source: https://github.com/weavejester/brutha

Global State management

atoms, cursors: OM, Js* libs

source: http://blog.getprismatic.com/content/images/2014/Jun/Cursor-example.png

Local State management

Cursors

Cursors allow com­po­nents to selec­tively update them­selves

Cursors propagate changes back to the original atom.
 

Each component gets a reference into the data store, and can update its own data using the cursor, transparently.


It isn't that components declare what data they need, but their parents provide them with a cursor when instantiating them.

Computed Observables

reagent

Automatic state propagation


Always reflect the latest state


Can depend on multiple atoms

 

Are themselves watchable

Mixins

RUM

Provides an API and set of building blocks to write components you need to. 


Lower-level details are well-defined and open for extensions. 


Uniform approach to manage application state.


Build your own storage model.

 

;; Regular static top-down component with immutable args

(rum/defc colored-clock < rum/static [time color]
  [:span {:style {:color color}} (ts->str time)])

(rum/defc reactive-timer < rum/reactive []
  [:div "Reactive: "
    ;; Subscribing to atom changes with rum/react
    ;; Then pass _values_ to static component
    (colored-clock (rum/react clock) (rum/react color))])

;; After initial mount, all changes will be re-rendered automatically
(rum/mount (reactive-timer) (el "reactive-timer"))
;; Reactive drawing board

(def rboard (atom (mixins/random-board)))
(def rboard-renders (atom 0))

(rum/defc rcell < rum/reactive [x y]
  (swap! rboard-renders inc)
  (let [cursor (rum/cursor rboard [y x])]
    ;; each cell subscribes to its own cursor inside a board
    ;; only if cell is on (@cursor == true),
    ;; this component will be notified on color changes
    [:div.art-cell {:style {:background-color (when (rum/react cursor) (rum/react color))}
                    :on-mouse-over (fn [_] (swap! cursor not) nil)}]))

(rum/defc art-rboard []
  [:div.artboard
    (for [y (range 0 utils/board-height)]
      [:div.art-row {:key y}
        (for [x (range 0 utils/board-width)]
          ;; this is how one can specify React key for component
          (rum/with-props rcell x y :rum/key [x y]))])
   (mixins/board-stats rboard rboard-renders)])

(rum/mount (art-rboard) (el "rboard"))

templates

JS: JSX
CLJs: sablono, kioo, flupot

HTML => CLJS => HTML

all HTML in CLJS

plain react DOM functions
BYO Template

data queries

datascripT

what if you your user-interface had a Database?

 

 

Every frontend app has an adhoc-state. Put it in a client-side in-memory database.


Whole DB Pre-fetch

Hierarchical, sparse, irregular, graph ...

Binary search, fast lookup, reverse iteration, 

 

Clients become decoupled and independent: rendering, server sync
Always consistent render

source: https://dl.dropboxusercontent.com/u/561580/conferences/2015.07%20polyconf.pdf

(rum/defc filter-pane [db]
  [:.filter-pane
    [:input.filter {:type "text"
                    :value (or (system-attr db :system/filter) "")
                    :on-change (fn [_]
                                 (set-system-attrs! :system/filter (dom/value (dom/q ".filter"))))
                    :placeholder "Filter"}]])

;; Rules are used to implement OR semantic of a filter
;; ?term must match either :project/name OR :todo/tags
(def filter-rule
 '[[(match ?todo ?term)
    [?todo :todo/project ?p]
    [?p :project/name ?term]]
   [(match ?todo ?term)
    [?todo :todo/tags ?term]]])

;; terms are passed as a collection to query,
;; each term futher interpreted with OR semantic
(defn todos-by-filter [db terms]
  (d/q '[:find [?e ...]
         :in $ % [?term ...]
         :where [?e :todo/text]
                (match ?e ?term)]
    db filter-rule terms))

Fetching data is hard, dealing with ever-changing data is hard, and performance is hard. Relay aims to reduce these problems to simple ones

 

Inspired by Flux

Instead of multiple stores, there is a central store caches all GraphQL data

Relay, GraphQL
OM.NEXT

A query engine to answer non-trivial questions about current app state.

 

Structured format to track data coming in and out of DB. Datalog queries can be run against it too.

wait, there's morE!
custom vdoms

in 328 lines of CLJS ... a prototype

~10 virtual Dom implementations in Js

remember

react is concerned with two things:

rendering STATE

communicating EVENTS

build your own "framework"

Building User Interfaces Should be fun again!

Thank you!

@priyatam

priyatam@facjure.com

https://slides.com/priyatam/react-life-without-mvc

References

http://facebook.github.io/react/
https://github.com/omcljs/om
https://github.com/reagent-project/reagent
https://github.com/tonsky/rum
https://github.com/tonsky/datascript

http://re-demo.s3-website-ap-southeast-2.amazonaws.com/
http://blog.getprismatic.com



a complete ui template in Cljs/Om:
https://github.com/priyatam/mala

  

React, Life without MVC

By Priyatam Mudivarti

React, Life without MVC

OO programming has many real benefits but one of its worst influences on UIs is obscuring Data. Yet, dozens of MVC frameworks continue to grow and introduce leaky abstractions with middlemen like Models, Views, Controllers and Directives. React is simple. Talk talk is a high-level overview on React's core principles, with an emphasis on Clojurescript’s abstractions like immutable data, state, and transitions.

  • 1,637