Fork me on GitHub
#re-frame
<
2023-08-04
>
Drew Verlee16:08:28

My codebase has some functionality that seems to be aimed at making it so that you request data from re-frame db will do so or set up a subscription (the later half being the novel part). The part that does that branching logic is in the get-in function below:

(ns re-frame-simple.db
  (:refer-clojure :exclude [get get-in assoc! identity swap!] :rename {get    get*
                                                                       get-in get-in*
                                                                       swap!  swap!*})
  (:require [re-frame.core :as rf]
            [re-frame.db :refer [app-db]]
            [reagent.ratom :as r])
  (:require-macros re-frame-simple.db))

(def ^:dynamic ^boolean *in-query?* false)

(defn get-in
  "Read a value from db by `path`, not-found or nil if value not present."
  ([path]
   (if *in-query?*
     (get-in* @app-db path)
     @(rf/subscribe [:get-in path])))
  ([path not-found]
   (if *in-query?*
     (get-in* @app-db path not-found)
     @(rf/subscribe [:get-in path not-found]))))
An example where "in-query?" is set to true:
(db/defquery body-classes []
  (let [intercom? (db/get-in [:db/intercom :active?])] <--- This will setup a subscription (code not shown it's a macro that creates a reg-sub-raw. 
    {:intercom-active intercom?}))

;; an example where in-query is false
(db/get-in [:db/routers router :path])
And I'm trying to get a sense of the trade-off in doing this. The benefit seems to be more concise code in cases where your subscription is simply a look up in the db (as opposed to needed some transformations after the data is fetched). So you don't have to repeat yourself between the subscription key (:users-followers) and the actual query path [:user :followers], like i this feels like:
(re-frame.core/reg-sub  
  :user-followers                  
  (fn [db query-v]      
    (get-in db [:user :followers]))   
Are their some obvious downsides to this approach? In the past, i have often wondered about doing just this, but i'm always hesitant to not go with the defaults a library, especially one involved in state management, provides, as it's easy to miss that some "boiler" aspects are really to provide an abstraction over that state management, and further obscuring them tends to lead to subtle but hard to understand translation issues.

p-himik17:08:30

Personally, I prefer chaining subs:

(rf/reg-sub :user :-> :user)
(rf/reg-sub :user-followers :<- [:user] :-> :followers)
or having my own wrapper that, when a vector follows :->, uses get-in instead of applying the fn:
(my-reg-sub :user-followers :-> [:user :followers])

Drew Verlee17:08:29

@U2FRKM4TW why do you prefer that? Does the chain allow for each link in the chain to update independently which leads to smaller and faster updates (at the expense of having to store more in memory?)

p-himik17:08:32

That (although there can easily be cases where it's slower, e.g. when you have many paths that don't share the root), an also it being simple and obvious.

👍 2