#{React, Reagent,

Re-frame}

A short overview

Why JS/CLJS frameworks?

  • Make it "easy" to manipulate a browser's DOM
  • Deal with I/O and other side-effects
  • Have data drive the UI

How is data driving the UI?

Pass in as properties

Have a DOM-like component tree structure and pass in data from the root

A.

B.

Side-load data

Components actively or reactively get data from a global service

C.

Local state

Keep some data, like the open/close status of a popup inside component

Data
  • Global vs. Local State
  • Aspiration: Same state -> same UI
  • Caveat: State in browser always stale
  • Keeping state in sync is hard

State is Data

React (class-based component)

React

React (functional component)

React

Reagent

Reagent
(defn hello-world []
  [:h1 {:color :green} "Hello World"])

(defn ^:export run []
  (rdom/render [hello-world] (js/document.getElementById "app")))

Sidebar Hiccup:

;;; tag opt: properties & children
[:h1 {:style {:color :black}} [:span "Something else"]]
;; ==> <h1 style="{color: 'black';}" <span>Something else</span></h1>

;;; with classes
[:h1.header.main "Meh"]


;;; reagent turns functions into react classes
(defn hello-world [name] [:span (str "Hi " name)])

[hello-world "folks"]

Reactions - the magic of Reagent

Reagent

Simple example:

(defonce time-color (r/atom "#f34"))

(defn color-input []
  [:div.color-input
   "Time color: "
   [:input {:type "text"
            :value @time-color
            :on-change #(reset! time-color (-> % .-target .-value))}]])
  • A hiccup function that (directly or indirectly) derefs a reaction/ratom is re-rendered when the data changes
  • That's the trick!
  • Combined with immutable data, can be faster than react itself

Re-frame - global state drives everything

Re-frame
  • The universe - as it pertains to the UI - exists in re-frame.db/app-db
  • Provides a "simple" mechanism to react to events
  • Runs side-effects separately
  • Changing app-db is the most common side-effect

Elements of a re-frame app

Re-frame

Elements of a re-frame app:

;;; event handlers - two flavors

(rf/reg-event-db 
 ::evt.setup
 (fn [db _] (assoc db :setup true)))
 
 (rf/reg-event-fx
 ::evt.setup
 (fn [{:keys [db]} _]))
 
 ;;; Subscriptions - three flavors (also reg-sub-raw)
 
 (rf/reg-sub
 ::sub.setup-names
 (fn [db _] (:setup-names db)))
 
 (rf/reg-sub
 ::sub.setup-names-url
 :<- [::sub.setup-names]
 (fn [names]
   (str (-> js/location .-origin) "/?p=" (some-> names (js/encodeURIComponent names)))))

(rf/reg-sub
 ::sub.setup-names-url
 (fn [_ _] (rf/subscribe [::sub.setup-names]))
 (fn [names]
   (str (-> js/location .-origin) "/?p=" (some-> names (js/encodeURIComponent names)))))

How components use re-frame

Re-frame

Elements of a re-frame app:

;;; use (rf/subscribe subs-vector) to create a reaction that reagent tracks
;;; i.e. if the data underlying ::sub.current-person changes, reagent will ask react to re-render current-person

(defn current-person []
  [:center [:h2 (or @(rf/subscribe [::sub.current-person]) "Done!")]])

;;; use (rf/dispatch event-vector) to dispatch an event

(defn action-bar []
  [:div
   (if @(rf/subscribe [::sub.has-next?]) 
     [:button {:on-click #(rf/dispatch [::evt.next!])} "Next!"]
     [:button.secondary {:on-click #(rf/dispatch [::evt.load-persons])} "Restart!"])])
Re-frame

React, Reagent, Re-frame

By Jochen Bedersdorfer

React, Reagent, Re-frame

  • 338