Fork me on GitHub
#re-frame
<
2024-03-04
>
lwhorton01:03:03

hey folks, apologies if this has been asked before but it's a little tricky to search for a concept. i'm wondering if there's a better way to handle this scenario: my goal is to expose interfaces to entities in my database only through subscriptions. i don't want to have to call a module's function and pass in the app-db. i exclusively want to use some well-defined, tightly controlled subs like [:entities/the-thing-by-id {:id ...}] . i think this helps decouple modules/namespaces and components, and generally keeps the whole system cleaner. this leads to an interesting (unlikable?) design choice : suppose my app is handling a collection of stores, where each store has departments and aisles. because i'm only exposing access to my 'base entities' through subscriptions, it makes my specific component subscriptions kind of akward. i feel like the view knows too much about what's happening in the rest of the app (why should this component have to know there's even a concept of a 'selected store').

(defn render-a-store
  (let [id @(rf/sub [:shared-state/the-selected-store])
        store @(rf/sub [:entities/store-by-id id])
        departments @(rf/sub [:component-state/store-departments id])
        aisles @(rf/sub [:component-state/store-aisles id])]
    [:div ,,,]))

;; what i have to do
(rf/reg-sub
 :component-state/store-aisles
 (fn [[_ store-id]]
   [(rf/sub [:entities/store-by-id store-id])])
 (fn [[store]]
   (sort (distinct (map :aisles store)))))

;; what i want to do, or something similar to this: feed one input signal into another
(rf/reg-sub
 :component-state/store-aisles
 (fn [[_ store-id]]
   [(rf/sub [:entities/store-by-id (rf/sub [:store/active-store-id])])])
 (fn [[store]]
   (sort (distinct (map :aisles store)))))
is there some way to shorten my sub-chain so my view can get a lot dumber?
(defn render-a-store
  (let [store @(rf/sub [:component-state/store])
        departments @(rf/sub [:component-state/store-departments])
        aisles @(rf/sub [:component-state/store-aisles])]
    [:div ,,,]))

lwhorton01:03:46

oh my goodness, this is exactly the point of reg-sub-raw right? it would be something like:

(rf/reg-sub-raw
 :component-state/store-aisles
 (fn [app-db event]
   (r/reaction
    (let [active-store-id @(rf/sub [:shared-state/the-selected-store])
          store @(rf/sub [:entities/store-by-id active-store-id])]
      (sort (distinct (map :aisles store)))))))
now that i look at it, i kind of wonder if my sub-only interface to entities is going to force me to write a lot of more-busy reg-sub-raw functions 😞. i think i should reconsider the way my signal graph works

p-himik08:03:04

Seems that it could be made much better by having a subscription that returns the active store itself, and use that as a signal to other subs. What also helps is being able to define signal functions elsewhere to reuse them:

(defn <-active-store-signal [_]
  [(rf/sub [...])])

(rf/reg-sub :store-aisles
  <-active-store-signal
  (fn [[store] _]
    (sort ...)))

👍 1