reactive programming in clojure script using reactjs wrappers

111
Reactive programming in ClojureScript using React.js wrappers Konrad Szydlo @ Lambda Days, Krakow, 19 February 2016

Upload: konrad-szydlo

Post on 21-Jan-2017

386 views

Category:

Technology


0 download

TRANSCRIPT

Page 1: Reactive programming in clojure script using reactjs wrappers

Reactive programming in ClojureScript using React.js wrappers

Konrad Szydlo @ Lambda Days, Krakow, 19 February 2016

Page 2: Reactive programming in clojure script using reactjs wrappers

State of the web development

More and more complex apps

Solving elaborate problems

Myriad of web browsers and devices

Performance and resources

Page 3: Reactive programming in clojure script using reactjs wrappers

Plain vanilla JavaScript

Page 4: Reactive programming in clojure script using reactjs wrappers

JQuery

Page 5: Reactive programming in clojure script using reactjs wrappers

MVC

Page 6: Reactive programming in clojure script using reactjs wrappers

React.js

Page 7: Reactive programming in clojure script using reactjs wrappers

What does it mean reactive?

Erik Meijer - What does it mean to be Reactive?

Lots of definitions

Difficult to agree

Page 8: Reactive programming in clojure script using reactjs wrappers

Components reacting to events and changing state

Page 9: Reactive programming in clojure script using reactjs wrappers

Apples

Oranges

Grapes

Page 10: Reactive programming in clojure script using reactjs wrappers

Add pineapple

But how exactly?

Page 11: Reactive programming in clojure script using reactjs wrappers

Solution #1

Insert a new list item

Find the right place

Page 12: Reactive programming in clojure script using reactjs wrappers

Solution #2

Throw out the entire list and rebuild it

Rendering large scale amounts of DOM is slow

Page 13: Reactive programming in clojure script using reactjs wrappers

Most JS frameworks help to mitigate problem #1

Make DOM easier to navigate

Tie element to a controller keeping it up to date

Page 14: Reactive programming in clojure script using reactjs wrappers

React solves problem #2

Page 15: Reactive programming in clojure script using reactjs wrappers

Virtual DOM (two copies)

Original

Updated

Page 16: Reactive programming in clojure script using reactjs wrappers

Compare new virtual DOM with previous

Render only bits that have changed

Page 17: Reactive programming in clojure script using reactjs wrappers

Pretending to re-render entire page is fast

Easier to reason about

Page 18: Reactive programming in clojure script using reactjs wrappers

V in MVC

Interfaces with changing data

Can combine with frameworks e.g. AngularJS

Page 19: Reactive programming in clojure script using reactjs wrappers

Flux architecture

Once-directional data flow

Action → Dispatcher → Store → View

Page 20: Reactive programming in clojure script using reactjs wrappers

JavaScript sucks

Page 21: Reactive programming in clojure script using reactjs wrappers

this

Verbose syntax

Confusing, inconsistent automatic operator conversions

NaN

Global variables by default

etc.

Page 22: Reactive programming in clojure script using reactjs wrappers

We need JavaScript

JS is (almost) everywhere

Runtime is improving

Web Sockets

WebRTC

Canvas

Push notifications

Page 23: Reactive programming in clojure script using reactjs wrappers

Build “better” language on top of JS

Page 24: Reactive programming in clojure script using reactjs wrappers

1 + 2 // simple, clear

(+ 1 2) ; OMG! So confusing

Clearly programmers aren't that smart as some think

Page 25: Reactive programming in clojure script using reactjs wrappers

ClojureScript

Modern LISP

Immutable data structures

State vs Identity

REPL

Transducers

core.async

And much much more

Page 26: Reactive programming in clojure script using reactjs wrappers

ClojureScript + React.js

Page 27: Reactive programming in clojure script using reactjs wrappers

Pure React.js is not idiomatic in ClojureScript

Leverage immutable data structures

Page 28: Reactive programming in clojure script using reactjs wrappers

ClojureScript React.js wrappers on steroids

Page 29: Reactive programming in clojure script using reactjs wrappers

Rum

Not a framework but a library

Page 30: Reactive programming in clojure script using reactjs wrappers

Om, Reagent, Quiescent have built-in component behaviour model

Page 31: Reactive programming in clojure script using reactjs wrappers

Rum

Two-level API

Page 32: Reactive programming in clojure script using reactjs wrappers

Rum

Define your component and render it

Page 33: Reactive programming in clojure script using reactjs wrappers

(rum/defc name doc-string? [params*] render-body+)

Page 34: Reactive programming in clojure script using reactjs wrappers

(rum/defc h1 [text] [:h1 text])

(rum/mount (h1 "important") js/document.body)

Page 35: Reactive programming in clojure script using reactjs wrappers

Simple component not reactive

Agnostic of data storage and behaviour model

Page 36: Reactive programming in clojure script using reactjs wrappers

Use mixins to control component's behaviour

Page 37: Reactive programming in clojure script using reactjs wrappers

Pre-built component mixins

Mimic behaviour of other wrappers

Page 38: Reactive programming in clojure script using reactjs wrappers

(rum/defc name doc-string? [< mixins+]? [params*] render-body+)

Page 39: Reactive programming in clojure script using reactjs wrappers

Static mixin

Checks if args have changed

Avoids re-rendering if they are the same

Page 40: Reactive programming in clojure script using reactjs wrappers

(rum/defc h1 < rum/static [text][:h1 text])

Page 41: Reactive programming in clojure script using reactjs wrappers

(rum/mount (h1 "important text") body)

(rum/mount (h1 "important text") body) ;; render won’t be called

(rum/mount (h1 "important") body) ;; this will cause re-rendering

Page 42: Reactive programming in clojure script using reactjs wrappers

Local Mixin

Rum local

Per-component local state

Page 43: Reactive programming in clojure script using reactjs wrappers
Page 44: Reactive programming in clojure script using reactjs wrappers
Page 45: Reactive programming in clojure script using reactjs wrappers
Page 46: Reactive programming in clojure script using reactjs wrappers

(rum/defcs make-service < (rum/local "") [state service](let [local (:rum/local state)]

[:p {:class @local

:on-click (fn [_] (swap! local toggle-class service))}

(:name service)

[:b (str " £" (:price service))]]))

Page 47: Reactive programming in clojure script using reactjs wrappers

(defn toggle-class [current-class service] (let [price (:price service)]

(if (= current-class "active")

(do (swap! order-form-total - price)

"")

(do (swap! order-form-total + price)

"active"))))

Page 48: Reactive programming in clojure script using reactjs wrappers

Reactive mixin

Creates “reactive” component

Tracks references to data

Auto-updates when value stored changes

Reactive mixin is like Reagent wrapper

Page 49: Reactive programming in clojure script using reactjs wrappers
Page 50: Reactive programming in clojure script using reactjs wrappers

(def order-form-total (atom 0))

(rum/defc total-sum < rum/reactive [] [:p {:id "total"} (str "Total: £" (rum/react order-form-total))])

Page 51: Reactive programming in clojure script using reactjs wrappers

(defn toggle-class [current-class service] (let [price (:price service)]

(if (= current-class "active")

(do (swap! order-form-total - price)

"")

(do (swap! order-form-total + price)

"active"))))

Page 52: Reactive programming in clojure script using reactjs wrappers

Cursored mixin

Interface to sub-trees inside an atom

Like an Om wrapper

Page 53: Reactive programming in clojure script using reactjs wrappers

(def state (atom {:app { :settings {:header {:colour "#cc3333" }}}}))

Page 54: Reactive programming in clojure script using reactjs wrappers

(rum/defc label < rum/cursored [colour text] [:label {:style {:colour @colour}} text])

Page 55: Reactive programming in clojure script using reactjs wrappers

(rum/defc body < rum/cursored [state] [:div

(label (rum/cursor state [:app :settings :headers :colour]) "First label" )])

Page 56: Reactive programming in clojure script using reactjs wrappers

(swap! state-cursor assoc-in [:app :settings :header :colour] "#cc56bb")

Page 57: Reactive programming in clojure script using reactjs wrappers

One global state

Atomic swaps

Always consistent

Easy to implement undo – reset to previous state

Serialize app state and send it over the wire

Page 58: Reactive programming in clojure script using reactjs wrappers

Custom mixin

Use React.js life-cycle functions

Will mount

Did mount

Should update

Will unmount

Page 59: Reactive programming in clojure script using reactjs wrappers

Scrolling mixin

Page 60: Reactive programming in clojure script using reactjs wrappers

(defn scroll-view [height] {:did-mount (fn [state]

(if-not (::scrolled state)

(do

(set-scroll-top! height)

(assoc state ::scrolled true))

state))

:transfer-state (fn [old new]

(assoc new ::scrolled (::scrolled old)

::with-scroll-view true))})

Page 61: Reactive programming in clojure script using reactjs wrappers

Build new mixins easily

Page 62: Reactive programming in clojure script using reactjs wrappers

Mix and match different mixins

Page 63: Reactive programming in clojure script using reactjs wrappers

The curious case of Om

Page 64: Reactive programming in clojure script using reactjs wrappers

The good, the bad and the future

Page 65: Reactive programming in clojure script using reactjs wrappers

Root UI is given root cursor

Passes sub-trees of cursors to its sub-components

Page 66: Reactive programming in clojure script using reactjs wrappers

{:list {:title "My list:" :colour :brown My list:

:items

{:item_1 {:text "text1" :colour :blue} - text1

:item_2 {:text "text2" :colour :green} - text2

:item_3 {:text "text3" :colour :red}}}} - text3

Page 67: Reactive programming in clojure script using reactjs wrappers

Case #1

UI cross-cutting concerns?

Page 68: Reactive programming in clojure script using reactjs wrappers

Dashboard with a list that can be expanded, shrank

Page 69: Reactive programming in clojure script using reactjs wrappers

Save the list presentation options to general settings?

Page 70: Reactive programming in clojure script using reactjs wrappers

Cursor-oriented architecture requires you to structure app state as a tree (matching UI)

Page 71: Reactive programming in clojure script using reactjs wrappers

Quite often your data are not a tree

Page 72: Reactive programming in clojure script using reactjs wrappers

Changing item's text is easy

Page 73: Reactive programming in clojure script using reactjs wrappers

Case #2

Deleting an item should be easy too, right?

Page 74: Reactive programming in clojure script using reactjs wrappers

Does delete button/option belong to an item or a list?

Page 75: Reactive programming in clojure script using reactjs wrappers

Deleting an item requires list manipulation

Page 76: Reactive programming in clojure script using reactjs wrappers

Rum is flexible use it if you can

Page 77: Reactive programming in clojure script using reactjs wrappers

Enter Om.next

New version of Om

Page 78: Reactive programming in clojure script using reactjs wrappers

Actually a reboot

Page 79: Reactive programming in clojure script using reactjs wrappers

Experience of the last few years

Inspired by:

Facebook's Relay

Netflix's Falcor

Cognitect's Datomic

Page 80: Reactive programming in clojure script using reactjs wrappers

Display date of past purchases of certain item

Page 81: Reactive programming in clojure script using reactjs wrappers

{:current-user

{:purchases

[{:id 146

:product-name "organic rice"

:date #inst "2015-12-17T17:13:59.167-00:00"

:previous {:id 140

:product-name "organic rice"

:date #inst "2015-12-14T17:05:58.144-00:00"

:previous {:id 136

:product-name "organic rice"

:date #inst "2015-12-10T17:05:13.512-00:00"

:previous ; and so on... }}}

{:id 145

:product-name "soy milk"

:date #inst "2015-12-17T17:09:25.961-00:00"

:previous {:id 141

:product-name "soy milk"

:date #inst "2015-12-15T17:05:36.797-00:00"

:previous {:id 128

:product-name "soy milk"

:date #inst "2015-12-07T17:04:51.124-00:00"

:previous ; and so on... }}}]}}

Page 82: Reactive programming in clojure script using reactjs wrappers

{:current-user {:recent-purchases [[:purchase-id 146]

[:purchase-id 145]]}

:purchased-items {146 {:id 146

:product-name "organic rice"

:date #inst "2015-12-17T17:13:59.167-00:00"

:previous [:purchase-id 144]}

145 {:id 145

:product-name "soy milk"

:date #inst "2015-12-17T17:09:25.961-00:00"

:previous [:purchase-id 143]}

144 {:id 144

:product-name "organic rice"

:date #inst "2015-12-17T17:05:58.144-00:00"

:previous [:purchase-id 141]}

143 {:id 143

:product-name "soy milk"

:date #inst "2015-12-17T17:05:36.797-00:00"

:previous [:purchase-id 138]}

;; and so on... }}

Page 83: Reactive programming in clojure script using reactjs wrappers

In Om.next components define a query to data they need

Page 84: Reactive programming in clojure script using reactjs wrappers

Cursor navigates a tree, query can navigate a graph

Page 85: Reactive programming in clojure script using reactjs wrappers

[{:current-user [{:recent-purchases [{:previous [:date]}]}]}]

Page 86: Reactive programming in clojure script using reactjs wrappers

{:current-user {:recent-purchases

[{:previous {:date #inst "2015-12-17T17:05:58.144-00:00"}}

{:previous {:date #inst "2015-12-17T17:05:36.797-00:00"}}]}}

Page 87: Reactive programming in clojure script using reactjs wrappers

{:current-user {:recent-purchases [[:purchase-id 146]

[:purchase-id 145]]}

:purchased-items {146 {:id 146

:product-name "organic rice"

:date #inst "2015-12-17T17:13:59.167-00:00"

:previous [:purchase-id 144]}

145 {:id 145

:product-name "soy milk"

:date #inst "2015-12-17T17:09:25.961-00:00"

:previous [:purchase-id 143]}

144 {:id 144

:product-name "organic rice"

:date #inst "2015-12-17T17:05:58.144-00:00"

:previous [:purchase-id 141]}

143 {:id 143

:product-name "soy milk"

:date #inst "2015-12-17T17:05:36.797-00:00"

:previous [:purchase-id 138]}

;; and so on... }}

Page 88: Reactive programming in clojure script using reactjs wrappers

{:current-user {:recent-purchases

[{:previous {:date #inst "2015-12-17T17:05:58.144-00:00"}}

{:previous {:date #inst "2015-12-17T17:05:36.797-00:00"}}]}}

Page 89: Reactive programming in clojure script using reactjs wrappers

Om.next normalizes data* you have into one you expect

* with a little help from you

Page 90: Reactive programming in clojure script using reactjs wrappers

{:list/one [{:product-name "soy milk" :price 10} {:product-name "eggs" :price 22}

{:product-name "juice" :price 26}]

:list/two [{:product-name "eggs" :price 22 :discount 7}

{:product-name "apples" :price 8}

{:product-name "cereals" :price 37 :discount 15}]}

Page 91: Reactive programming in clojure script using reactjs wrappers

(defui Product static om/Ident

(ident [this {:keys [product-name]}]

[:product/by-name product-name])

static om/IQuery

(query [this]

'[:name :price :discount])

Object

(render [this]

;; ... elided ...))

Page 92: Reactive programming in clojure script using reactjs wrappers

(d{:list/one [[:product/by-name "soy milk"]

[:product/by-name "eggs"]

[:product/by-name "juice"]],

:list/two

[[:product/by-name "eggs"]

[:product/by-name "apples"]

[:product/by-name "cereals"]],

:product/by-name

{"soy milk" {:name "soy milk", :price 10},

"eggs" {:name "eggs", :price 22, :discount 7},

"juice" {:name "juice", :price 26}, "apples" {:name "apples", :price 8},

"cereals" {:name "cereals", :price 37, :discount 15}}

Page 93: Reactive programming in clojure script using reactjs wrappers

Store data the way you want and let Om.next convert data to match your UI

Page 94: Reactive programming in clojure script using reactjs wrappers

Om.next

Currently in alpha stage

Production ready in a couple of months

Page 95: Reactive programming in clojure script using reactjs wrappers

Datascript

Because good things come in pairs

Nikita Prokopov creator of Rum and Datascript

Page 96: Reactive programming in clojure script using reactjs wrappers

Immutable in-memory database

Datalog as a query engine

Page 97: Reactive programming in clojure script using reactjs wrappers

Central, uniform approach to manage all application state

Page 98: Reactive programming in clojure script using reactjs wrappers

Schemaless DB < Datascript < Relational schema

Data already normalized

Page 99: Reactive programming in clojure script using reactjs wrappers

{:db/id #db/id[:db.part/db]

:db/ident :product/name

:db/valueType :db.type/string

:db/cardinality :db.cardinality/one

:db/unique :db.unique/identity

:db/doc "Product's name"}

{:db/id #db/id[:db.part/db]

:db/ident :product/price

:db/valueType :db.type/number

:db/cardinality :db.cardinality/one

:db/doc "Product's price"}

Page 100: Reactive programming in clojure script using reactjs wrappers

Datalog query

[:find ?name ?price ?discount :where [?p :product/name ?name] [?p :product/price ?price] [?p :product/discount ?discount]]

Page 101: Reactive programming in clojure script using reactjs wrappers

(defui Product static om/Ident

(ident [this {:keys [product-name]}]

[:product/by-name product-name])

static om/IQuery

(query [this]

'[:name :price :discount])

Object

(render [this]

;; ... elided ...))

Page 102: Reactive programming in clojure script using reactjs wrappers

Find specific item

[:find ?name ?price ?discount :in $ ?name :where [?p :product/name ?name] [?p :product/price ?price] [?p :product/discount ?discount]]

Page 103: Reactive programming in clojure script using reactjs wrappers

(rum/defc product-item [db name]

(let [item (d/q '[:find ?name ?price ?discount :in $ ?name :where [?p :product/name ?name] [?p :product/price ?price] [?p :product/discount ?discount]] db name)]

(when item

[:p name "$" (:product/price item) "discount $" (:product/discount item)])))

Page 104: Reactive programming in clojure script using reactjs wrappers

Write components that depend on return values of queries

Page 105: Reactive programming in clojure script using reactjs wrappers

Database as a value

Page 106: Reactive programming in clojure script using reactjs wrappers

Summary

React.js is interesting – give it a go

ClojureScript is interesting too

React.js + ClojureScript = awesome

Interesting wrappers for React.js in ClojureScript with some cool technologies

Page 107: Reactive programming in clojure script using reactjs wrappers

Questions???

Page 108: Reactive programming in clojure script using reactjs wrappers

Tweet me: @ryujinkony

Konrad Szydlogithub

retailic.com

Page 109: Reactive programming in clojure script using reactjs wrappers

Retailic

Retailic helps retail to draw business conclusions from consumer behaviour and develop corresponding software solutions.

Page 110: Reactive programming in clojure script using reactjs wrappers

Resources

https://www.youtube.com/watch?v=sTSQlYX5DU0 Eric Meijer

https://medium.com/@carloM/reactive-programming-with-kefir-js-and-react-a0e8bb3af636#.td9mdlfzr Reactive programming with react.js

http://blog.ractivejs.org/posts/whats-the-difference-between-react-and-ractive/ React.js vs Ractive

http://www.funnyant.com/reactjs-what-is-it/

Page 111: Reactive programming in clojure script using reactjs wrappers

Resources

https://www.destroyallsoftware.com/talks/wat

https://github.com/active-group/reacl

https://github.com/reagent-project/reagent

https://github.com/tonsky/rum

https://github.com/tonsky/datascript

https://github.com/omcljs/om/wiki