Reagent FAQs

Back to SIG ClojureScript Tutorials

Author: Gary Johnson

Date: 2021-07-20

1. Why must we call Reagent components inside of square brackets?

For example:

(defn title []
  [:h1 "Name of App"])


(defn app []
  [:div {:class "app"}
   [title]])

This is the mechanism that Reagent uses for lazy evaluation of function components. When we wrap our functions in parens, we are (of course) calling them and returning the result, which will be inlined at the call site into any containing Hiccup forms. When we wrap our functions in vectors, we are (as per Clojure's evaluation rules) not calling them. Instead we are simply creating a vector containing a function and its arguments. When Reagent is walking our Hiccup tree, it will store each such function-containing vector as a separate component. When your app state is updated, only those components which dereference that state are rerendered into the DOM.

2. Does calling rdom/render multiple times override any previous rendering?

A call to rdom/render replaces the content of the selected DOM node with the nodes generated by your component function. The operative word is "replaces". You only want to call rdom/render once on your root component.

3. When would you want to use a r/cursor instead of a r/atom?

A r/cursor provides an atom-like view of a subset of a r/atom's contents. If a component only dereferences a cursor, then it will only be re-rendered whenever that subset of the atom is updated with the reset! or swap! functions.

4. What's the difference between a Reagent component that just returns some Hiccup and a Reagent component that returns a function?

A function that takes or returns another function is called a higher order function. In Reagent, we essentially have state, render functions, and lifecycle methods. If all of your state is kept at the global level, most of your render functions will probably just need to return some Hiccup. However, you may choose to move some of your application state within a component function. To do so, you use a lexical closure:

(defn component-with-state []
  (let [secret-number (atom (rand-int 100))]
    (fn []
      [:input {:type      "text"
               :value     @secret-number
               :on-change (fn [e] (reset! secret-number (-> e .-target .-value)))}])))

This allows us to perform component initialization steps that will only happen the first time component-with-state is rendered. Going forward, whenever a change is made to any app state that this component references (i.e., secret-number), then the lexical closure returned by component-with-state will be called as the new render function.

5. What does the ^:export metadata tag do to a function?

During advanced mode compilation, the names of your Clojurescript symbols will be renamed into something short and unrecognizable in order to minimize the size of the JS bundle file that the user's web browser has to download. Attaching :export metadata to a var prevents this renaming from happening. You usually just need to do this for functions that you mean to call from