Skip to content

Macros WIP

Tim Gilbert edited this page Apr 4, 2015 · 6 revisions

We haven't formalised any macros yet, but we're making a collection of useful snippets here ...

Writing Reagent Components

Credit: @rsslldnphy

You might need to write this component:

(defn user-details
  []
  (let [user (subscribe [:current-user])]
    (fn []                       ;; sometimes you forget the extra function layer
      [:div
        [:h1 (:name @user)]      ;; easy to forget the @ on user
        [:p  (:bio @user)]]))

Some common pitfalls are noted in comments.

Here's an alternative way:

(defn user-details
  []
  (with-subs [user [:current-user]]      ;; say what subscriptions you want here
    [:div
      [:h1 (:name user)]                 ;; look - using 'user' with no leading @
      [:p  (:bio user)]]))

Here's how you do it:

(defn- to-sub
  [[binding sub]]
  `[~binding (re-frame.core/subscribe ~sub)])

(defn- to-deref
  [binding]
  `[~binding (deref ~binding)])

(defmacro with-subs
  [bindings & body]
  `(let [~@(apply concat (map to-sub (partition 2 bindings)))]
     (fn []
       (let [~@(apply concat (map to-deref (take-nth 2 bindings)))]
         ~@body))))

There's another subtle reason to like this macro: If ever you subscribe in a component, but then never use the result of the subscription, you'll have created a small memory leak.

(def my-component
   []
   (let [name (subscribe [:a :b])]   ;; subscribe here
      (fn []
        [:div "hello"])))          ;; oops I never used @name in the renderer

The fix is obviously to remove this unused subscription. But because this macro explicitly "derefs" all subscription results (eg; name above), in the renderer, there'll be no memory leak. But you'll still have a slightly harmless bug, because you have declared a subscription you never use.