Fork me on GitHub
#re-frame
<
2017-07-12
>
stuartrexking05:07:01

@mikethompson I think I just had that moment of enlightenment, which is to store ALL state in app-db, even the flow through the screens, views.

stuartrexking05:07:23

And position through the flows, steps, etc

mikethompson06:07:19

That is absolutely the right path. Everything goes in apps-lib everytime. Very occasionally we have to use component local state, and I never feel good about it.

stuartrexking06:07:26

Thanks again. You are a champ!

mikethompson06:07:24

@souenzzo remember that a Level 2 subscription won't recalculate if its inputs are the same as last time

mikethompson06:07:58

;; Level 1
;; just extract, no computation 
(reg-sub 
   :result 
   (fn  [db _]
      (:result db)))

;; Level 2
;; do expensive computation, each time the input signal changes
(reg-sub 
    :expensive
    (fn [_ _] 
         (subscribe [:result]))
    (fn [result _]
         ... expensive bit of work done on new result))

mikethompson06:07:49

That level 2 subscription won't run unless :result changes in app-db

mikethompson06:07:06

Either that or compute the expensive thing in an event handler and store it in app-db

mbertheau08:07:57

@mikethompson Thanks! Any idea about the double evaluation of Layer-1 subs?

mbertheau08:07:33

(Btw. this is not critical for my app; I was just restructuring subscriptions such that there's more of a tree dependency-wise and not all subs depend directly on app-db. I noticed the double evals during debugging and was wondering about them.)

mikethompson12:07:16

Can't think of a reason for double evals.

jhund15:07:29

@mikethompson just wanted to give you some documentation feedback. I’ve read the entire documentation a few times, and just had a lightbulb moment when I read your comments above about Level 1 and Level 2 subscriptions: The purpose of level 1 subscriptions is to improve code performance around level 2 subscriptions: Whenever I have an expensive computation (level 2), I try to run it as rarely as possible. If a level 2 subscription that performs an expensive computation were to use app-db as an input signal (i.e. deref it), it would run every time anything in app-db changes. This is something we want to avoid at all costs. So instead of using the entire app-db as an input signal, we create level 1 subscriptions for each subset of app-db that is relevant to the expensive computation. Now the level 2 subscription will be re-computed only when any of the relevant values change. We’ve eliminated wasteful computation. Not sure why it only clicked now. Maybe it’s me having an enlightened day. You’ve done an outstanding job with the documentation!

musheddev15:07:19

@jhund @mikethompson We simplified the amount of times level 1 subscriptions trigger using reagent cursors. `(defn reg-sub-for-path [k path f] (rf/reg-sub-raw k (fn [db _] (reaction (f @(ra/cursor db path))))))`

yedi17:07:23

hey guys, i'm partially re writing a react-redux app in re-frame. I want to keep using the JS react components we already have in our app, but render them via a re-frame shell application

yedi17:07:26

however, since im using react-redux-router, they use Providers stores and connect() to handle global app state. It's possible to create my own custom store that is hooked up to the re-frame app-db

yedi17:07:57

however, the store requires that you can subscribe to changes made to it's state, so my question is:

yedi17:07:17

is there a way to subscribe to changes made in re-frame's app-db ratom

jhund18:07:15

@musheddev thanks for the tip. Do you have any metrics for how much using cursors impacts performance?

jhund18:07:40

Are there any tradeoffs?

musheddev18:07:25

@jhund I don't know of any issues, though I have wondered what the trade off is. Level 1 subscriptions wont scale well given n events (db updates) a second and m level one subscriptions we'd have n*m instances of unnecessary code running a second (differentiation and copying of data). Whether the reagent cursor has a similar scaling problem, I don't know but I think It may be a little better.

lwhorton23:07:04

hey is there a way to subscribe and immediately shove that sub result back into the db?

lwhorton23:07:45

for example, if you have some data representing a tree, and then multiple implementations of that tree… the data behind the tree never changes (or if it does, everyone should get the new data), but each tree implementation might have things like {:expanded? true :label "foo"}

lwhorton23:07:35

I tried doing a derived-only-data tree, but it gets really hairy quite quickly.. and feels kind of hacky trying to synchronize two dependent states

musheddev23:07:27

@lwhorton what I think you want is to split some data up in the db and then recombine them in subscriptions

musheddev23:07:02

a db like {:tree 'tree-data' :tree-instances {1 {:expanded? true :label "foo"}}

musheddev23:07:32

a subscription like (reg-sub :tree-instance (fn [] 'sub to tree-data' 'sub to tree instance) (fn [[tree-data tree-instance] ] (produce useful data for rendering a specific instance)).

mikethompson23:07:19

@lwhorton You have options ... 1. use enrich or on-change or some post-processing interceptor 2.. don't shove a value back into app-db, instead dispatch the change (re-frame is event driven) via reagent's track

(defonce tracking-x-y reagent.core/track
  (fn [] 
     (let  [x-val  @(re-frame.core/subscribe [:query-x])
            y-val @(re-frame.core/subscribe [:query-y])]
        (re-frame.core/dispatch [:something changed x-val y-val]))))

lwhorton23:07:13

interesting .. didn’t know tracking was a thing but that looks like what i’m after

lwhorton23:07:44

also the two separate subs -> merge into proper state seems plausible, though perhaps a bit more wordy

lwhorton23:07:28

after walking on it, I wonder if there’s a simpler way to do updates/changes to one structure depending on a diff between the two… some sort of multimethod dispatching on the diff.

lwhorton23:07:03

i’m always looking for ways to go from (fn do-something [a b] ...) to {:do-something [a b]}