React, Data Binding and Sugar

Sweetened Data Binding in React

I will show a small example of data binding in React and break down the ClojureScript sugared syntax.

Reagent binds components to data using atoms. An atom is a ClojureScript reference type. It references an immutable value. However the reference itself is mutable, it can be changed using reset! and swap! to reference a different imumtable value. Changes to atom’s are atomic.

The value of an atom can be retrived by dereferencing the atom using deref or the @ prefix, for example @my-atom.

Functions can be attached to atoms to watch for changes, the watcher function will get the atoms old and new state as arguments.

Reagent provides its own atom, reagent/atom, which has watches setup to rerender compontents which dereference the atom.

;; An atom for application state, initially set to an empty string
(defonce person-name (reagent/atom ""))

;; A component
(defn home-page []
  [:div
    [:input { :type :text :value @person-name :name "person_name" :on-change #(reset! person-name (-> % .-target .-value))}]
    [:div { :id "person_name" } @person-name]])
    
;; Render the component to the "app" element
(reagent/render [current-page] (.getElementById js/document "app"))

The rendered DOM would look something like:

<div id="app">
  <div>
    <input type="text" name="person_name" value="">
    <div></div>
  </div>
</div>

In the actual HTML you might If you type in the input the div below the input will be updated as you type.

The function bound to :on-change receives a Javascript event. We must mutate the atom “on change” otherwise the component will keep rendering with the same atom value and it will appear that you can’t type characters in to the input.

The function looks like this: #(reset! person-name (-> % .-target .-value).

Let’s break this down…

#(...) is syntactical sugar for a function, it equates to (fn [x] ...). The function input is bound to %, in the expanded version it appears explicitly as x.

reset! is used to change the value the atom references, the first argument is the atom, the second the new value it should reference.

An example would be (reset! person-name "Kris").

The -> is a thread operator, it chains several functions together the output of each given as the input in to the next.

For example (-> [1 2 3] first str) would expand to (str (first [1 2 3])), both returning "1".

In some cases using -> makes the code easier to read.

The .- syntax is part of Javascript interoprability and fetches an attribute, for example .-target % would return the “target” attribute of the given event, in our case the input element.

Combining -> and .- we get the ‘target’ attribute from the given event, which is the input, then the value attribute of the input.

If you expanded our entire on-change function it would look like this:

(fn [event] (reset! person-name (.-value (.-target event))))

When updating the atom any compontent that references it is re-rendered, in our case the div below the input. Therefore if we use a reagent/atom to store our state all compontents which reference the atom will be automatically re-rendered when it changes.

Addendum

You can actually chain the Javascript attribute’s, so instead of (-> % .-target .-value) you could write (.-target.value %). Which you use is personal choice.