+ All Categories
Home > Technology > High Performance web apps in Om, React and ClojureScript

High Performance web apps in Om, React and ClojureScript

Date post: 06-May-2015
Category:
Upload: leonardo-borges
View: 6,575 times
Download: 2 times
Share this document with a friend
Description:
Talk given at YOW! LambdaJam, Brisbane 2014
43
High Performance Web UI's with Om and React LambdaJam - Brisbane, 2014 Leonardo Borges @leonardo_borges www.leonardoborges.com www.thoughtworks.com
Transcript
Page 1: High Performance web apps in Om, React and ClojureScript

High Performance Web UI's with Om and React

LambdaJam - Brisbane, 2014

Leonardo Borges @leonardo_borges

www.leonardoborges.com www.thoughtworks.com

Page 2: High Performance web apps in Om, React and ClojureScript

About‣ ThoughtWorker ‣ Functional Programming & Clojure advocate ‣ Founder of the Sydney Clojure User Group

‣ Currently writing “Clojure Reactive Programming”

Page 3: High Performance web apps in Om, React and ClojureScript

Functional Programming is on the rise

Page 4: High Performance web apps in Om, React and ClojureScript

And that makes us happy

Page 5: High Performance web apps in Om, React and ClojureScript

However if you do client-side web development, you’re out of luck

Page 6: High Performance web apps in Om, React and ClojureScript

Because Javascript…

[1,3,5].map(parseInt);!// [1, NaN, NaN]!

Page 7: High Performance web apps in Om, React and ClojureScript

There are options

Page 8: High Performance web apps in Om, React and ClojureScript

Today we’ll see one of them: Clojurescript

Page 9: High Performance web apps in Om, React and ClojureScript

What we’ll see

‣ An Overview of React and how it enables fast rendering ‣ Meet Om, a ClojureScript interface to React ‣ Boosting React’s rendering performance with immutable data structures ‣ A simple demo featuring “data binding” and undo functionality

Page 10: High Performance web apps in Om, React and ClojureScript

React

‣ Created by Facebook for building user interfaces ‣ The V in MVC ‣ Self-contained components ‣ Doesn’t make assumptions about your stack - can be used with anything

Page 11: High Performance web apps in Om, React and ClojureScript

Self-contained components

‣ React combines display logic and DOM generation ‣ Components are themselves loosely coupled ‣ The whole app is re-rendered on every update ‣ Virtual DOM ‣ Efficient diff algorithm

Page 12: High Performance web apps in Om, React and ClojureScript

Efficient diff algorithm

‣ Creates a virtual version of the DOM ‣ As the application state changes new DOM trees are generated ‣ React diffs the trees and computes the minimal set of changes ‣ Finally it applies the changes to the real DOM

Page 13: High Performance web apps in Om, React and ClojureScript

A simple React componentvar HelloMessage = React.createClass({! displayName: 'HelloMessage',! render: function() {! return React.DOM.div(null, "Hello ", this.props.name);! }!});!!

React.renderComponent(! HelloMessage( {name:"John"} ), mountNode);

Page 14: High Performance web apps in Om, React and ClojureScript

No literals?

Page 15: High Performance web apps in Om, React and ClojureScript

A simple React component (using the JSX pre-processor)

var HelloMessage = React.createClass({! render: function() {! return <div>Hello {this.props.name}</div>;! }!});!!

React.renderComponent(! <HelloMessage name="John" />, mountNode);

Page 16: High Performance web apps in Om, React and ClojureScript

React components are functions from application state to a DOM tree

Page 17: High Performance web apps in Om, React and ClojureScript

Now let’s take a leap and look at the same component, written in Om

Page 18: High Performance web apps in Om, React and ClojureScript

A simple Om component(def app-state (atom {:name "Leo"}))!!

(defn hello-message [app owner]! (reify om/IRender! (render [_]! (dom/div nil! (str "Hello " (:name app))))))!!

!

(om/root hello-message app-state! {:target (. js/document (getElementById "hello"))})!

Page 19: High Performance web apps in Om, React and ClojureScript

Om/React’s component lifecycle

IWillMountIInitState IShouldUpdate

IRender

IRenderState

Page 20: High Performance web apps in Om, React and ClojureScript

IShouldUpdate

‣ Called on app state changes but before rendering ‣ This is where React uses its fast diff algorithm ‣ Om components implement the fastest algorithm possible: a simple reference equality check ‣ Generally, you won’t have to implement this

Page 21: High Performance web apps in Om, React and ClojureScript

IInitState & IRenderState

‣ Initialise component local state using IInitState ‣ Use IRenderState to work with it and render the component

Page 22: High Performance web apps in Om, React and ClojureScript

IInitState & IRenderState(defn counter [app owner]! (reify! om/IInitState! (init-state [_]! {:clicks 0})! om/IRenderState! (render-state [_ state]! (dom/div nil! (str "Clicks " (:clicks state))! (dom/button #js {:onClick! #(om/set-state! owner :clicks (inc (:clicks state)))}! "Click me!")))))!!(om/root counter (atom {})! {:target (. js/document (getElementById "app"))})!

Page 23: High Performance web apps in Om, React and ClojureScript

IRender

‣ Same as IRenderState… ‣ …except it doesn’t depend on the component local state to render

Page 24: High Performance web apps in Om, React and ClojureScript

IRender(def app-state (atom {:name "Leo"}))!!

(defn hello-message [app owner]! (reify om/IRender! (render [_]! (dom/div nil! (str "Hello " (:name app))))))!!

!

(om/root hello-message app-state! {:target (. js/document (getElementById "hello"))})!

Page 25: High Performance web apps in Om, React and ClojureScript

A larger example

Page 26: High Performance web apps in Om, React and ClojureScript

A larger example

Page 27: High Performance web apps in Om, React and ClojureScript

A reusable editable component(defn editable [text owner]! (reify! om/IInitState! (init-state [_]! {:editing false})! om/IRenderState! (render-state [_ {:keys [editing]}]! (dom/li nil! (dom/span #js {:style (display (not editing))} (om/value text))! (dom/input! #js {:style (display editing)! :value (om/value text)! :onChange #(handle-change % text owner)! :onKeyPress #(when (== (.-keyCode %) 13)! (commit-change text owner))! :onBlur (fn [e] (commit-change text owner))})! (dom/button! #js {:style (display (not editing))! :onClick #(om/set-state! owner :editing true)}! "Edit")))))!

From https://github.com/swannodette/om/wiki/Basic-Tutorial

Page 28: High Performance web apps in Om, React and ClojureScript

A reusable editable component(defn editable [text owner]! (reify! om/IInitState! (init-state [_]! {:editing false})! om/IRenderState! (render-state [_ {:keys [editing]}]! (dom/li nil! (dom/span #js {:style (display (not editing))} (om/value text))! (dom/input! #js {:style (display editing)! :value (om/value text)! :onChange #(handle-change % text owner)! :onKeyPress #(when (== (.-keyCode %) 13)! (commit-change text owner))! :onBlur (fn [e] (commit-change text owner))})! (dom/button! #js {:style (display (not editing))! :onClick #(om/set-state! owner :editing true)}! "Edit")))))!

From https://github.com/swannodette/om/wiki/Basic-Tutorial

Page 29: High Performance web apps in Om, React and ClojureScript

A reusable editable component(defn editable [text owner]! (reify! om/IInitState! (init-state [_]! {:editing false})! om/IRenderState! (render-state [_ {:keys [editing]}]! (dom/li nil! (dom/span #js {:style (display (not editing))} (om/value text))! (dom/input! #js {:style (display editing)! :value (om/value text)! :onChange #(handle-change % text owner)! :onKeyPress #(when (== (.-keyCode %) 13)! (commit-change text owner))! :onBlur (fn [e] (commit-change text owner))})! (dom/button! #js {:style (display (not editing))! :onClick #(om/set-state! owner :editing true)}! "Edit")))))!

From https://github.com/swannodette/om/wiki/Basic-Tutorial

Page 30: High Performance web apps in Om, React and ClojureScript

The speakers view(defn speakers-view [app owner]! (reify! om/IRender! (render [_]! (dom/div nil! (dom/div #js {:id "speakers"! :style #js {:float "left"}}! (dom/h2 nil "Speakers")! (dom/button #js {:onClick undo} "Undo")! (dom/button #js {:onClick reset-app-state} "Reset")! (apply dom/ul nil! (om/build-all speaker-view (speakers app)! {:shared {:app-state app}})))! (om/build speaker-details-view app)))))

This is how you build components

Page 31: High Performance web apps in Om, React and ClojureScript

The Sessions view

Same deal as before

(defn sessions-view [app owner]! (reify! om/IRender! (render [_]! (dom/div #js {:id "sessions"}! (dom/h2 nil "Sessions")! (apply dom/ul nil! (map #(om/build editable %) (vals (:sessions app))))))))!

Page 32: High Performance web apps in Om, React and ClojureScript

Apps can have multiple roots

(om/root speakers-view app-state! {:target (. js/document (getElementById "speakers"))})!!

(om/root sessions-view app-state! {:target (. js/document (getElementById "sessions"))})!

You can have multiple “mini-apps” inside your main app

Makes it easy to try Om in a specific section/feature

Page 33: High Performance web apps in Om, React and ClojureScript

What about “undo” and “reset”?

Page 34: High Performance web apps in Om, React and ClojureScript

Implementing undo(def app-state (atom speaker-data))!(def app-history (atom [@app-state]))!!

(add-watch app-state :history! (fn [_ _ _ n]! (when-not (= (last @app-history) n)! (swap! app-history conj n))! (let [c (count @app-history)]! (prn c " Saved items in app history"))))!!

(defn undo []! (when (> (count @app-history) 1)! (swap! app-history pop)! (reset! app-state (last @app-history))))!

Page 35: High Performance web apps in Om, React and ClojureScript

Implementing reset

(defn reset-app-state []! (reset! app-state (first @app-history))! (reset! app-history [@app-state]))!

Page 36: High Performance web apps in Om, React and ClojureScript

Om/React components are functions from state to DOM trees

Page 37: High Performance web apps in Om, React and ClojureScript

With immutable data structures we can access every version of the application state

Page 38: High Performance web apps in Om, React and ClojureScript

So we simply update the application state, causing the components to get re-rendered

Page 39: High Performance web apps in Om, React and ClojureScript

A bit of live coding

Page 40: High Performance web apps in Om, React and ClojureScript

Summary

‣ With Om, you’re not using a crippled template language, you can leverage all of Clojurescript (including other DOM libraries) ‣ Rendering and display logic are inevitably coupled. Om/React acknowledges that a bundles them in components ‣ The whole app is re-rendered on every state change, making it easier to reason about ‣ This is efficient thanks to immutable data structures

Page 41: High Performance web apps in Om, React and ClojureScript

Summary

‣ Clojurescript also provides a better development experience with a powerful browser REPL much like what you’d get with Clojure on the JVM ‣ Source maps are here today ‣ Bottom line is that Clojurescript is a serious contender

Page 42: High Performance web apps in Om, React and ClojureScript

References

‣ Code: https://github.com/leonardoborges/lambdajam-2014-om-talk ‣ React documentation: http://facebook.github.io/react/ ‣ Om documentation: https://github.com/swannodette/om/wiki/Documentation#build ‣ Basic Tutorial: https://github.com/swannodette/om/wiki/Basic-Tutorial

Page 43: High Performance web apps in Om, React and ClojureScript

Thanks! Questions?

Leonardo Borges @leonardo_borges

www.leonardoborges.com www.thoughtworks.com


Recommended