The Case for

React.js and ClojureScript


Murilo Pereira

@mpereira

May 2, 2014

Problem

Building UIs is difficult.

Modern Web UIs


  • visual representations of data changing over time
  • respond to asynchronous user events
  • transition both the underlying data and itself to new states

"Data changing over time is the root of all evil."

Incidental Complexity


  • JavaScript isn't reactive
  • DOM is stateful

What if the JavaScript DOM API was reactive?


function tweetScore(tweet) {
  return(tweet.favorites_count + tweet.retweets_count);
}

function compareTweetsByScore(a, b) {
  return(tweetScore(b) - tweetScore(a));
}

function renderApplication(tweets) {
  return(document.dom.ul(
    { class: 'tweets' },
    tweets
      .sort(compareTweetsByScore)
      .slice(0, 5)
      .map(function(tweet) {
        return(document.dom.li({ class: 'tweet' }, tweet.text);
      })
  ));
}

var tweets = fetchTweets({ username: 'mpereira' }, { limit: 20 });
document.dom.render(renderApplication(tweets), document.body);
        

Possible solution: Data binding

Data Binding


  • Observables
  • Computed properties


Backbone, Ember, Meteor, et al.

Contemporary data binding is not simple.

Simple


Not to be mistaken for "easy"


Easiness = Familiarity (subjective)


Simplicity = "Does/is one thing" (objective)

Data Binding


  • Application logic entangled with observables
  • Forces us to compose our programs with framework constructs instead of language constructs (functions and data structures)

"How often are you fighting the framework?"

Dirty-checking (Angular)


Also susceptible to the problems of data binding.











https://docs.angularjs.org/guide/concepts

Complex

Even with shortcomings it's still possible to build complex, modern UIs using contemporary MVC frameworks.

"We can create precisely the same programs we're creating right now with drastically simpler tools."

Rich Hickey

A different solution to the same problem.

React.js

React.js


  • Library for creating UIs
  • Renders the DOM and responds to user events
  • Can be thought of as the V in MVC

Remember our utopic example a few slides back?


function tweetScore(tweet) {
  return(tweet.favorites_count + tweet.retweets_count);
}

function compareTweetsByScore(a, b) {
  return(tweetScore(b) - tweetScore(a));
}

function renderApplication(tweets) {
  return(document.dom.ul(
    { class: 'tweets' },
    tweets
      .sort(compareTweetsByScore)
      .slice(0, 5)
      .map(function(tweet) {
        return(document.dom.li({ class: 'tweet' }, tweet.text);
      })
  ));
}

var tweets = fetchTweets({ username: 'mpereira' }, { limit: 20 });
document.dom.render(renderApplication(tweets), document.body);
        

It's actually valid React.js code


function tweetScore(tweet) {
  return(tweet.favorites_count + tweet.retweets_count);
}

function compareTweetsByScore(a, b) {
  return(tweetScore(b) - tweetScore(a));
}

function renderApplication(tweets) {
  return(React.DOM.ul(
    { className: 'tweets' },
    tweets
      .sort(compareTweetsByScore)
      .slice(0, 5)
      .map(function(tweet) {
        return(React.DOM.li({ className: 'tweet' }, tweet.text);
      })
  ));
}

var tweets = fetchTweets({ username: 'mpereira' }, { limit: 20 });
React.renderComponent(renderApplication(tweets), document.body);

React gives us a minimally leaky abstraction for a reactive JavaScript/DOM environment.

Data Component DOM
Model View DOM
M3 V2 DOM M2 M4 M5 M1 V1 V3
Data C3 DOM C4 C2 C5 C1

Components

Components


Idempotent functions that describe your UI at any point in time.

component(data) = VDOM

component(data_1) = VDOM_1


*user input changes data from data_1 to data_2*


component(data_2) = VDOM_2

diffVDOMs(VDOM_1, VDOM_2) = diff

DOMOperations(diff) = operations

applyDOMOperations(operations, document.body)

Best part? You don't even have to worry about this. Just build components.

Every place data is displayed is guaranteed to be up-to-date.


No need for KVO or marking HTML templates with framework directives.

Frees the programmer from doing manual, explicit DOM operations.

But what about the performance?


Isn't diffing VDOMs slow?


Why have VDOMs if the browser already has a DOM?

The DOM is slow.

The DOM is slow


  • Querying may require tree traversal
  • Mutations trigger viewport reflows, relayouts, repaints (CSS engine, etc.)
  • Can also invalidate caches, requiring the entire DOM to be reconstructed on the viewport

Having in-memory representations of the DOM allows React to have extremely fast UIs.

Virtual DOM (VDOM)


  • Allows for components to have a declarative API
  • Performs the minimum amount of actual DOM operations through computing diffs
  • Handles DOM operations for you

"Performance isn't the [main] goal of the VDOM, but if you're worried with performance, most workloads are as fast or faster [than the MVC alternatives] out of the box."

Pete Hunt

Components

Components allow you to express your program in the language of your problem domain.


Rather than on the language of a particular framework.

Bill O'Reilly
Tide comes in, tide goes out. You can't explain that!
[Not supported by viewer]
Top Tweets
Just had #breakfast.
[Not supported by viewer]
@troll yes, definitely plan on that.
[Not supported by viewer]
pic.twitter.com/asd23 #selfie #instagram
[Not supported by viewer]
Good# morning.
[Not supported by viewer]
Bill O'ReillyTop Tweets
Tide comes in, tide goes out. You can't explain that! #tides
[Not supported by viewer]
Just had #breakfast.
[Not supported by viewer]
@troll yes, definitely plan on that.
[Not supported by viewer]
pic.twitter.com/asd23 #selfie #instagram
[Not supported by viewer]
Good# morning.
[Not supported by viewer]
HeaderProfileTweetsTop Tweets

var Profile = React.createClass({
  render: function() {
    return(React.DOM.div(null, [
      React.DOM.img(null, this.props.user.image),
      React.DOM.p(null, this.props.user.name)
    ]);
  }
});

var Tweets = React.createClass({
  render: function() {
    return(React.DOM.ul(null, this.props.tweets.map(function(tweet) {
      return(React.DOM.li(null, tweet.text));
    });
  }
});

var TopTweets = React.createClass({
  render: function() {
    return(React.DOM.div(null, [
      React.DOM.h1(null, 'Top Tweets'),
      Profile({ user: this.props.user }),
      Tweets({ tweets: this.props.user.tweets })
    ]);
  }
});

React.renderComponent(TopTweets({ user: user }), document.body);
        


var Profile = React.createClass({
  render: function() {
    return(
      <div>
        <img href={this.props.user.image} />
        <p>{this.props.user.name}</p>
      </div>
    );
  }
});

var Tweets = React.createClass({
  render: function() {
    return(
      <ul>
        {this.props.tweets.map(function(tweet) {
          return(<li>tweet.text</li>);
        })};
      </ul>
    );
  }
});

var TopTweets = React.createClass({
  render: function() {
    return(
      <div>
        <h1>Top Tweets</h1>
        <Profile user={this.props.user} />
        <Tweets tweets={this.props.user.tweets} />
      </div>
    );
  }
});

React.renderComponent(TopTweets({ user: user }), document.body);

TopTweets(data) = Header() + Profile(data_1) + Tweets(data_2)

Components are reusable and composable declarative representations of your UI.

Collateral Benefits

Collateral Benefits


  • Render the application on the server (node.js, Java 8's Nashhorn, etc.)
  • Run the entire application in a Web Worker
  • Render the VDOM to Canvas, SVG, etc.
  • UI testability for free
  • No templates!

React.js takeaways

  • Declarative, fast UIs
  • Express your programs in the language of your problem domain

ClojureScript

Problem



JavaScript sucks


We need JavaScript

JavaScript sucks


  • No integers
  • No module system
  • Verbose syntax
  • Confusing, inconsistent equality operators
  • Confusing, inconsistent automatic operator conversions
  • Lack of block scope
  • Non-uniform iterators
  • Global variables by default
  • NaN
  • this
  • No macros (yet...)
  • etc.

We Need JavaScript


  • Runtime is everywhere
  • Runtime is improving (Web Sockets, geo-location, FS API, RAF, push notifications, WebRTC, WebGL, SVG, Canvas, Web Workers, IndexedDB, etc.)

Build better languages on top of JavaScript.

Compilers!


  • Lots of them already exist
  • Only a few bring significant improvements



Popular ones


  • CoffeeScript: mostly syntax sugar
  • TypeScript: typed superset of JavaScript. Brings type checking, compile time errors

ClojureScript

ClojureScript is a Clojure compiler that targets JavaScript.

Why ClojureScript?

Why Clojure?

Why LISP?

Clojure is just a better language.

Number of days spent designing the language v1 JavaScript Clojure

ClojureScript


  • LISP
  • STM
  • Runtime polymorphism
  • The REPL
  • Functional programming
  • Immutable data structures
  • Uniform API over immutable data structures
  • Macros
  • Lazy sequences
  • Destructuring
  • State VS Identity
  • etc.

core.async



(go (try
  (let [tweets    (<? (get-tweets-for "swannodette"))
        first-url (<? (expand-url (first (parse-urls tweets))))
        response  (<? (http-get first-url))]
    (. js/console (log "Most recent link text:" response)))
  (catch js/Error e
    (. js/console (error "Error with the twitterverse:" e)))))
        


Asynchronous Error Handling

The possibility of having access to the power of Clojure in the browser is immensely valuable.

Value


To programmers, who will be able to build better programs, with less bugs, faster.


To the people and companies who will benefit from those programs.

The developer experience has improved significantly.

Developer Experience


  • Source maps
  • Easily reproducible tutorials
  • Browser-connected in-editor REPLs
  • Fast compilation cycles
  • Compatible libraries

Recommend you to check it out

(but you probably have, already)


Om

ClojureScript interface to React.

"Because of immutable data Om can deliver even better performance than React out of the box."


David Nolen

???

ClojureScript

  • Compiled, slower than hand-written JavaScript
  • Uses immutable data structures, slower than JavaScript data structures

And yet

Om's performance beats that of most JavaScript MVC frameworks

"Ideas have fundamental performance properties."

Pete Hunt

Immutable data structures make React's diffing algorithm really smart.

And more...

Takeaways

"Ideas have fundamental performance properties."

"Don't trade simplicity for familiarity."

And give it five minutes.

Thanks!

@mpereira

Learn React.js


Why?


How

Learn ClojureScript


Why?


How