SlideShare a Scribd company logo
ClojureScript
interfaces to React
Michiel Borkent
@borkdude
Øredev, November 6th 2014
Michiel Borkent (@borkdude)
● Clojure(Script) developer at
● Clojure since 2009
● Former lecturer, taught Clojure
Full Clojure stack example @ Finalist
Commercial app.
Fairly complex UI
● Menu: 2 "pages"
Page 1:
Dashboard. Create new or select
existing entity to work on.
Then:
● Wizard 1
○ Step 1..5
○ Each step has a
component
● Wizard 1 - Step2
○ Wizard 2
■ Step 1'
■ Step 2'
Full Clojure stack examples @ Finalist
Step 2 of inner wizard:
● Three dependent dropdowns
+ backing ajax calls
● Crud table of added items +
option to remove
● When done: create
something based on all of
this on server and reload
entire "model" based on
what server says
Because of React + Om we didn't
have to think about updating DOM
performantly or keeping "model" up
to date.
Agenda
● What is React?
● Om
● Reagent
What is React?
React
● Developed by Facebook
● Helps building reusable and composable UI
components
● Unidirectional Data Flow
● Less need for re-rendering logic
● Leverages virtual DOM for performance
● Can render on server to make apps crawlable
/** @jsx React.DOM */
var Counter = React.createClass({
getInitialState: function() {
return {counter: this.props.initialCount};
},
inc: function() {
this.setState({counter: this.state.counter + 1});
},
render: function() {
return <div>
{this.state.counter}
<button onClick={this.inc}>x</button>
</div>;
}
});
React.renderComponent(<Counter initialCount={10}/>, document.body);
ClojureScript interfaces
Prior knowledge
(def my-atom (atom 0))
@my-atom ;; 0
(reset! my-atom 1)
(reset! my-atom (inc @my-atom)) ;; bad idiom
(swap! my-atom (fn [old-value]
(inc old-value)))
(swap! my-atom inc) ;; same
@my-atom ;; 4
Before React: manual DOM edits
(add-watch greeting-form :form-change-key
(fn [k r o n]
(dispatch/fire :form-change {:old o :new n})))
(dispatch/react-to #{:form-change}
(fn [_ m]
(doseq [s (form-fields-status m)]
(render-form-field s))
(render-button [(-> m :old :status)
(-> m :new :status)] )))
source: http://clojurescriptone.com/documentation.html
ClojureScript interfaces
Quiescent - Luke vanderHart
Om - David Nolen
Reagent (was: Cloact) - Dan Holmsand
React + ClojureScript
Both Om and Reagent leverage:
● immutability for faster comparison in
shouldComponentUpdate
● Fewer redraws by batching updates with
requestAnimationFrame
Om
● Opinionated library by David Nolen
● One atom for app state
● Props: narrowed scope of app state (cursor)
(def app-state (atom {:counter1 {:count 10}
:counter2 {:count 11}}))
(defn main [app owner]
(om/component
(dom/div nil
(om/build counter (:counter1 app))
(om/build counter (:counter2 app)))))
Om
● Communication between components via
○ setting init-state / state (parent -> child)
○ callbacks (child -> parent)
○ app-state
○ core.async
● Explicit hooks into React lifecycle via ClojureScript
protocols
● Follows React semantics closely (e.g. local state
changes cause re-rendering)
(def app-state (atom {:counter 10}))
(defn app-state-counter [app owner]
(reify
om/IRender
(render [_]
(dom/div nil
(:counter app)
(dom/button
#js {:onClick
#(om/transact! app :counter inc)}
"x")))))
(om/root
app-state-counter
app-state
{:target (. js/document (getElementById "app"))})
Goya pixel editor
Some catches
● Large vocabulary around cursors: app(-state), owner, build,
cursors, ref-cursors, root, update!, update-state!,
transact!, opts
● Cursor behaves differently depending on lifecycle
● Strong correspondence between component tree structure and app state
structure (ref-cursors are supposed to solve this)
● Heavy use of callbacks or core.async to make components reusable
(should not rely on app-state)
● Omission of #js reader literal, :className instead of :class, or nil if no
attributes used, fails silently or cryptic error messages
Reagent
Reagent
Uses RAtoms for state management
Components are 'just functions'™ that
● must return something renderable by React
● can deref RAtom(s)
● can accept props as args
● may return a closure, useful for setting up initial state
Reagent
● Components should be called like
[component args] instead of
(component args)
● Components are re-rendered when
○ props (args) change
○ referred RAtoms change
● Hook into React lifecycle via metadata on component functions
(def component
(with-meta
(fn [x]
[:p "Hello " x ", it is " (:day @time-state)])
{:component-will-mount #(println "called before mounting")
:component-did-update #(js/alert "called after updating")} ))
(def count-state (atom 10))
(defn counter []
[:div
@count-state
[:button {:on-click #(swap! count-state inc)}
"x"]])
(reagent/render-component [counter]
(js/document.getElementById "app"))
RAtom
(defn local-counter [start-value]
(let [count-state (atom start-value)]
(fn []
[:div
@count-state
[:button {:on-click #(swap! count-state inc)}
"x"]])))
(reagent/render-component [local-counter 10]
(js/document.getElementById "app"))
local
RAtom
CRUD!
(def animals-state (atom #{}))
(go (let [response
(<! (http/get "/animals"))
data (:body response)]
(reset! animals-state (set data))))
RAtom with set containing
animal hash-maps
(...
{:id 2,
:type :animal,
:name "Yellow-backed duiker",
:species "Cephalophus silvicultor"}
{:id 1,
:type :animal,
:name "Painted-snipe",
:species "Rostratulidae"}
Render all animals from state
(defn animals []
[:div
[:table.table.table-striped
[:thead
[:tr
[:th "Name"] [:th "Species"] [:th ""] [:th ""]]]
[:tbody
(map (fn [a]
^{:key (str "animal-row-" (:id a))}
[animal-row a])
(sort-by :name @animals-state))
[animal-form]]]])
a row component
for each animal
form to create new animal
key needed for React to keep track of rows
ClojureScript interfaces to React
(defn animal-row [a]
(let [row-state (atom {:editing? false
:name (:name a)
:species (:species a)})
current-animal (fn []
(assoc a
:name (:name @row-state)
:species (:species @row-state)))]
(fn []
[:tr
[:td [editable-input row-state :name]]
[:td [editable-input row-state :species]]
[:td [:button.btn.btn-primary.pull-right
{:disabled (not (input-valid? row-state))
:onClick (fn []
(when (:editing? @row-state)
(update-animal! (current-animal)))
(swap! row-state update-in [:editing?] not))}
(if (:editing? @row-state) "Save" "Edit")]]
[:td [:button.btn.pull-right.btn-danger
{:onClick #(remove-animal! (current-animal))}
"u00D7"]]])))
(defn field-input-handler
"Returns a handler that updates value in atom map,
under key, with value from onChange event"
[atom key]
(fn [e]
(swap! atom
assoc key
(.. e -target -value))))
(defn input-valid? [atom]
(and (seq (-> @atom :name))
(seq (-> @atom :species))))
(defn editable-input [atom key]
(if (:editing? @atom)
[:input {:type "text"
:value (get @atom key)
:onChange (field-input-handler atom key)}]
[:p (get @atom key)]))
(defn remove-animal! [a]
(go (let [response
(<! (http/delete (str "/animals/"
(:id a))))]
(if (= (:status response)
200)
(swap! animals-state remove-by-id (:id a))))))
(defn update-animal! [a]
(go (let [response
(<! (http/put (str "/animals/" (:id a))
{:edn-params a}))
updated-animal (:body response)]
(swap! animals-state
(fn [old-state]
(conj
(remove-by-id old-state (:id a))
updated-animal))))))
replace updated
animal retrieved
from server
if server says:
"OK!", remove
animal from
CRUD table
Live demo
If you want to try yourself. Code and slides at:
https://github.com/borkdude/oredev2014
My experience with Om and Reagent
● Both awesome
● Added value to React
● Om encourages snapshot-able apps but:
○ surprises
○ large vocabulary
● Reagent
○ easy to learn and use
○ readable

More Related Content

PDF
ClojureScript for the web
PDF
Kotlin Developer Starter in Android projects
PDF
Kotlin advanced - language reference for android developers
PDF
Dragoncraft Architectural Overview
PDF
ClojureScript: The Good Parts
PDF
Mobile Day - React Native
PDF
Developing JavaScript Widgets
ODP
Asynchronous I/O in NodeJS - new standard or challenges?
ClojureScript for the web
Kotlin Developer Starter in Android projects
Kotlin advanced - language reference for android developers
Dragoncraft Architectural Overview
ClojureScript: The Good Parts
Mobile Day - React Native
Developing JavaScript Widgets
Asynchronous I/O in NodeJS - new standard or challenges?

What's hot (20)

PDF
Clojurian Conquest
PDF
Kotlin in action
PDF
Skiron - Experiments in CPU Design in D
PDF
Streams in Node.js
PDF
Best Practices in Qt Quick/QML - Part III
 
PDF
JDKs 10 to 14 (and beyond)
PDF
Serving QML applications over the network
PPTX
Return of c++
PDF
Kotlin hands on - MorningTech ekito 2017
PDF
TypeScript Introduction
PDF
Android antipatterns
PDF
A Recovering Java Developer Learns to Go
PDF
Swift - One step forward from Obj-C
PDF
06 - Qt Communication
PDF
Develop your next app with kotlin @ AndroidMakersFr 2017
PDF
Vladymyr Bahrii Understanding polymorphism in C++ 16.11.17
ODP
AST Transformations at JFokus
PDF
Seeking Clojure
PDF
Practical REPL-driven Development with Clojure
PDF
JavaScript in 2016
Clojurian Conquest
Kotlin in action
Skiron - Experiments in CPU Design in D
Streams in Node.js
Best Practices in Qt Quick/QML - Part III
 
JDKs 10 to 14 (and beyond)
Serving QML applications over the network
Return of c++
Kotlin hands on - MorningTech ekito 2017
TypeScript Introduction
Android antipatterns
A Recovering Java Developer Learns to Go
Swift - One step forward from Obj-C
06 - Qt Communication
Develop your next app with kotlin @ AndroidMakersFr 2017
Vladymyr Bahrii Understanding polymorphism in C++ 16.11.17
AST Transformations at JFokus
Seeking Clojure
Practical REPL-driven Development with Clojure
JavaScript in 2016
Ad

Similar to ClojureScript interfaces to React (20)

PDF
High Performance web apps in Om, React and ClojureScript
PDF
Introduction to Functional Reactive Web with Clojurescript
PDF
Integrating React.js with PHP projects
PPTX
Battle of React State Managers in frontend applications
PDF
An Intense Overview of the React Ecosystem
PDF
Reactjs: Rethinking UI Devel
PDF
Enjoyable Front-end Development with Reagent
PDF
Om nom nom nom
PDF
ClojureScript - Making Front-End development Fun again - John Stevenson - Cod...
PDF
Workshop 19: ReactJS Introduction
PPTX
React outbox
PDF
React Lifecycle and Reconciliation
PDF
React js
PDF
An Overview of the React Ecosystem
PDF
I Heard React Was Good
PDF
The Road To Redux
PDF
React for Dummies
PDF
Server side rendering with React and Symfony
PPTX
React + Redux Introduction
PDF
Render-as-You-Fetch
High Performance web apps in Om, React and ClojureScript
Introduction to Functional Reactive Web with Clojurescript
Integrating React.js with PHP projects
Battle of React State Managers in frontend applications
An Intense Overview of the React Ecosystem
Reactjs: Rethinking UI Devel
Enjoyable Front-end Development with Reagent
Om nom nom nom
ClojureScript - Making Front-End development Fun again - John Stevenson - Cod...
Workshop 19: ReactJS Introduction
React outbox
React Lifecycle and Reconciliation
React js
An Overview of the React Ecosystem
I Heard React Was Good
The Road To Redux
React for Dummies
Server side rendering with React and Symfony
React + Redux Introduction
Render-as-You-Fetch
Ad

Recently uploaded (20)

PDF
PTS Company Brochure 2025 (1).pdf.......
PPTX
L1 - Introduction to python Backend.pptx
PDF
System and Network Administration Chapter 2
PPTX
Transform Your Business with a Software ERP System
PDF
T3DD25 TYPO3 Content Blocks - Deep Dive by André Kraus
PPTX
Agentic AI Use Case- Contract Lifecycle Management (CLM).pptx
PPTX
ai tools demonstartion for schools and inter college
PDF
Internet Downloader Manager (IDM) Crack 6.42 Build 41
PDF
Addressing The Cult of Project Management Tools-Why Disconnected Work is Hold...
PDF
Why TechBuilder is the Future of Pickup and Delivery App Development (1).pdf
PDF
Upgrade and Innovation Strategies for SAP ERP Customers
PDF
Nekopoi APK 2025 free lastest update
PDF
Odoo Companies in India – Driving Business Transformation.pdf
PDF
Raksha Bandhan Grocery Pricing Trends in India 2025.pdf
PPTX
Essential Infomation Tech presentation.pptx
PPTX
Agentic AI : A Practical Guide. Undersating, Implementing and Scaling Autono...
PDF
top salesforce developer skills in 2025.pdf
PDF
medical staffing services at VALiNTRY
PDF
Flood Susceptibility Mapping Using Image-Based 2D-CNN Deep Learnin. Overview ...
PPTX
Operating system designcfffgfgggggggvggggggggg
PTS Company Brochure 2025 (1).pdf.......
L1 - Introduction to python Backend.pptx
System and Network Administration Chapter 2
Transform Your Business with a Software ERP System
T3DD25 TYPO3 Content Blocks - Deep Dive by André Kraus
Agentic AI Use Case- Contract Lifecycle Management (CLM).pptx
ai tools demonstartion for schools and inter college
Internet Downloader Manager (IDM) Crack 6.42 Build 41
Addressing The Cult of Project Management Tools-Why Disconnected Work is Hold...
Why TechBuilder is the Future of Pickup and Delivery App Development (1).pdf
Upgrade and Innovation Strategies for SAP ERP Customers
Nekopoi APK 2025 free lastest update
Odoo Companies in India – Driving Business Transformation.pdf
Raksha Bandhan Grocery Pricing Trends in India 2025.pdf
Essential Infomation Tech presentation.pptx
Agentic AI : A Practical Guide. Undersating, Implementing and Scaling Autono...
top salesforce developer skills in 2025.pdf
medical staffing services at VALiNTRY
Flood Susceptibility Mapping Using Image-Based 2D-CNN Deep Learnin. Overview ...
Operating system designcfffgfgggggggvggggggggg

ClojureScript interfaces to React

  • 1. ClojureScript interfaces to React Michiel Borkent @borkdude Øredev, November 6th 2014
  • 2. Michiel Borkent (@borkdude) ● Clojure(Script) developer at ● Clojure since 2009 ● Former lecturer, taught Clojure
  • 3. Full Clojure stack example @ Finalist Commercial app. Fairly complex UI ● Menu: 2 "pages" Page 1: Dashboard. Create new or select existing entity to work on. Then: ● Wizard 1 ○ Step 1..5 ○ Each step has a component ● Wizard 1 - Step2 ○ Wizard 2 ■ Step 1' ■ Step 2'
  • 4. Full Clojure stack examples @ Finalist Step 2 of inner wizard: ● Three dependent dropdowns + backing ajax calls ● Crud table of added items + option to remove ● When done: create something based on all of this on server and reload entire "model" based on what server says Because of React + Om we didn't have to think about updating DOM performantly or keeping "model" up to date.
  • 5. Agenda ● What is React? ● Om ● Reagent
  • 7. React ● Developed by Facebook ● Helps building reusable and composable UI components ● Unidirectional Data Flow ● Less need for re-rendering logic ● Leverages virtual DOM for performance ● Can render on server to make apps crawlable
  • 8. /** @jsx React.DOM */ var Counter = React.createClass({ getInitialState: function() { return {counter: this.props.initialCount}; }, inc: function() { this.setState({counter: this.state.counter + 1}); }, render: function() { return <div> {this.state.counter} <button onClick={this.inc}>x</button> </div>; } }); React.renderComponent(<Counter initialCount={10}/>, document.body);
  • 10. Prior knowledge (def my-atom (atom 0)) @my-atom ;; 0 (reset! my-atom 1) (reset! my-atom (inc @my-atom)) ;; bad idiom (swap! my-atom (fn [old-value] (inc old-value))) (swap! my-atom inc) ;; same @my-atom ;; 4
  • 11. Before React: manual DOM edits (add-watch greeting-form :form-change-key (fn [k r o n] (dispatch/fire :form-change {:old o :new n}))) (dispatch/react-to #{:form-change} (fn [_ m] (doseq [s (form-fields-status m)] (render-form-field s)) (render-button [(-> m :old :status) (-> m :new :status)] ))) source: http://clojurescriptone.com/documentation.html
  • 12. ClojureScript interfaces Quiescent - Luke vanderHart Om - David Nolen Reagent (was: Cloact) - Dan Holmsand
  • 13. React + ClojureScript Both Om and Reagent leverage: ● immutability for faster comparison in shouldComponentUpdate ● Fewer redraws by batching updates with requestAnimationFrame
  • 14. Om ● Opinionated library by David Nolen ● One atom for app state ● Props: narrowed scope of app state (cursor) (def app-state (atom {:counter1 {:count 10} :counter2 {:count 11}})) (defn main [app owner] (om/component (dom/div nil (om/build counter (:counter1 app)) (om/build counter (:counter2 app)))))
  • 15. Om ● Communication between components via ○ setting init-state / state (parent -> child) ○ callbacks (child -> parent) ○ app-state ○ core.async ● Explicit hooks into React lifecycle via ClojureScript protocols ● Follows React semantics closely (e.g. local state changes cause re-rendering)
  • 16. (def app-state (atom {:counter 10})) (defn app-state-counter [app owner] (reify om/IRender (render [_] (dom/div nil (:counter app) (dom/button #js {:onClick #(om/transact! app :counter inc)} "x"))))) (om/root app-state-counter app-state {:target (. js/document (getElementById "app"))})
  • 18. Some catches ● Large vocabulary around cursors: app(-state), owner, build, cursors, ref-cursors, root, update!, update-state!, transact!, opts ● Cursor behaves differently depending on lifecycle ● Strong correspondence between component tree structure and app state structure (ref-cursors are supposed to solve this) ● Heavy use of callbacks or core.async to make components reusable (should not rely on app-state) ● Omission of #js reader literal, :className instead of :class, or nil if no attributes used, fails silently or cryptic error messages
  • 20. Reagent Uses RAtoms for state management Components are 'just functions'™ that ● must return something renderable by React ● can deref RAtom(s) ● can accept props as args ● may return a closure, useful for setting up initial state
  • 21. Reagent ● Components should be called like [component args] instead of (component args) ● Components are re-rendered when ○ props (args) change ○ referred RAtoms change ● Hook into React lifecycle via metadata on component functions (def component (with-meta (fn [x] [:p "Hello " x ", it is " (:day @time-state)]) {:component-will-mount #(println "called before mounting") :component-did-update #(js/alert "called after updating")} ))
  • 22. (def count-state (atom 10)) (defn counter [] [:div @count-state [:button {:on-click #(swap! count-state inc)} "x"]]) (reagent/render-component [counter] (js/document.getElementById "app")) RAtom
  • 23. (defn local-counter [start-value] (let [count-state (atom start-value)] (fn [] [:div @count-state [:button {:on-click #(swap! count-state inc)} "x"]]))) (reagent/render-component [local-counter 10] (js/document.getElementById "app")) local RAtom
  • 24. CRUD!
  • 25. (def animals-state (atom #{})) (go (let [response (<! (http/get "/animals")) data (:body response)] (reset! animals-state (set data)))) RAtom with set containing animal hash-maps (... {:id 2, :type :animal, :name "Yellow-backed duiker", :species "Cephalophus silvicultor"} {:id 1, :type :animal, :name "Painted-snipe", :species "Rostratulidae"}
  • 26. Render all animals from state (defn animals [] [:div [:table.table.table-striped [:thead [:tr [:th "Name"] [:th "Species"] [:th ""] [:th ""]]] [:tbody (map (fn [a] ^{:key (str "animal-row-" (:id a))} [animal-row a]) (sort-by :name @animals-state)) [animal-form]]]]) a row component for each animal form to create new animal key needed for React to keep track of rows
  • 28. (defn animal-row [a] (let [row-state (atom {:editing? false :name (:name a) :species (:species a)}) current-animal (fn [] (assoc a :name (:name @row-state) :species (:species @row-state)))] (fn [] [:tr [:td [editable-input row-state :name]] [:td [editable-input row-state :species]] [:td [:button.btn.btn-primary.pull-right {:disabled (not (input-valid? row-state)) :onClick (fn [] (when (:editing? @row-state) (update-animal! (current-animal))) (swap! row-state update-in [:editing?] not))} (if (:editing? @row-state) "Save" "Edit")]] [:td [:button.btn.pull-right.btn-danger {:onClick #(remove-animal! (current-animal))} "u00D7"]]])))
  • 29. (defn field-input-handler "Returns a handler that updates value in atom map, under key, with value from onChange event" [atom key] (fn [e] (swap! atom assoc key (.. e -target -value)))) (defn input-valid? [atom] (and (seq (-> @atom :name)) (seq (-> @atom :species)))) (defn editable-input [atom key] (if (:editing? @atom) [:input {:type "text" :value (get @atom key) :onChange (field-input-handler atom key)}] [:p (get @atom key)]))
  • 30. (defn remove-animal! [a] (go (let [response (<! (http/delete (str "/animals/" (:id a))))] (if (= (:status response) 200) (swap! animals-state remove-by-id (:id a)))))) (defn update-animal! [a] (go (let [response (<! (http/put (str "/animals/" (:id a)) {:edn-params a})) updated-animal (:body response)] (swap! animals-state (fn [old-state] (conj (remove-by-id old-state (:id a)) updated-animal)))))) replace updated animal retrieved from server if server says: "OK!", remove animal from CRUD table
  • 31. Live demo If you want to try yourself. Code and slides at: https://github.com/borkdude/oredev2014
  • 32. My experience with Om and Reagent ● Both awesome ● Added value to React ● Om encourages snapshot-able apps but: ○ surprises ○ large vocabulary ● Reagent ○ easy to learn and use ○ readable