there will be no maths ...
spaghetti
two-way data flow
rampant mutation
expensive change tracking
performance
* yes ours too
- Rich Hickey -
FP programs are compositions of pure functions with strictly contained side effects
Pure functions are easy to test, easy to parallelise, easy to refactor
FP programs are predictable
FP favours immutable data
An app or site is a composition of UI components
A UI component is a pure function that accepts application state and returns a representation of the UI
Same state -> same UI
Different state -> new UI
Facebook with React framework?
Gives us a one way data flow
Components can be regarded as pure functions
Virtual DOM is the much copied secret sauce
One way flow + vdom + immutable data == FAST
* puke
Very well designed simple lisp - we all “transpile*” anyway
powerful meta programming capabilities
code is data
“Isomorphic*”
Functional slant
Immutable by default with good concurrency primitives
Live coding
Fun
Hiccup - html is code not a string (can be transformed)
(
func
(
func2 arg arg)
)
arg
start s-expression
first form is always a function
then come the arguments
which can be function calls
#
(
func3 arg arg)
or lambdas
close s-expression
Notice that this whole thing is itself a list:
codeception
A super simple wrapper around react
Very nice hiccup style templating
(defn simple-parent []
[:div
[:p "I include simple-component."]
[simple-component]])(defn hello-component [name]
[:p "Hello, " name "!"])
(defn say-hello []
[hello-component "world"])(ns example
(:require [reagent.core :as reagent :refer [atom]]))
(def click-count (atom 0))
(defn counting-component []
[:div
"The atom " [:code "click-count"] " has value: "
@click-count ". "
[:input {:type "button" :value "Click me!"
:on-click #(swap! click-count inc)}]])(ns example
(:require [reagent.core :as reagent :refer [atom]]))
(defn atom-input [value]
[:input {:type "text"
:value @value
:on-change #(reset! value (-> % .-target .-value))}])
(defn shared-state []
(let [val (atom "foo")]
(fn []
[:div
[:p "The value is now: " @val]
[:p "Change it here: " [atom-input val]]])))provides a simple and magical way to manage state
;;subscriptions
(register-sub :paging
(fn [db _]
(reaction (:paging @db)))))
;;component
(defn pager []
(let [paging (subscribe [:paging])]
(fn []
(let [index (:index @paging)
size (:size @paging)
total (:total @paging)
pages (.ceil js/Math (/ total size))
start (* index size)
end (+ start size)]
[:ul.pager
[:li {:className (if (= index 0) "disabled") }
[:a {:on-click #(when (> index 0) ( dispatch [:change-page dec]))
:href "#"} [:i.fa.fa-arrow-left] " Previous"]]
[:li.record-count (str "page " (inc index) " of " pages " (" total " resources)")]
[:li {:className (if (= pages (inc index)) "disabled") }
[:a {:on-click #(when (< (inc index) pages) ( dispatch [:change-page inc]))
:href "#"} "Next " [:i.fa.fa-arrow-right]]]]))))
;;message handler
(register-handler
:change-page
(fn [db [_ op]]
(let [i (get-in db [:paging :index])
paging (assoc (get db :paging) :index (op i))]
(search (:search db) paging :search-results)
(assoc db :paging paging))))
Not everything is awesome
testing
debugging
THE BRACKETS!
learning curve
Yes and no
Maybe
Probably not
https://github.com/clojure/clojurescript
https://github.com/Day8/re-frame
http://facebook.github.io/react/
https://reagent-project.github.io/index.html
References