re-frame

https://github.com/Day8/re-frame/blob/master/docs/README.md https://github.com/Day8/re-frame/blob/master/docs/External-Resources.md
superstructor 2021-04-21T00:18:58.221800Z

You probably want to upgrade 10x as that version is not officially compatible, assuming they are using a version of reagent/react equally as new as re-frame 1.1.2; ref https://github.com/day8/re-frame-10x#compatibility-matrix

kennytilton 2021-04-21T05:40:09.222200Z

Thx, yeah, I saw that low version on 10x. tbh, I never wrapped my head around 10x as a debugging tool. But worth doing anyway as long as we have the beast in there. 🀷 New around here, do not want to make waves. 🌊

Stuart Campbell 2021-04-21T06:00:26.225Z

I'm using re-frame-10x in a project that has a main module and a web worker module. The relevant part of shadow.cljs.edn looks something like:

:builds {:main {:target :browser
                :devtools {:preloads [day8.re-frame-10x.preload]}
                :modules {:common {:entries []}
                          :main {:init-fn foo.core/init
                                 :depends-on #{:common}}
                          :worker {:init-fn foo.worker/init
                                   :depends-on #{:common}
                                   :web-worker true}}}}
I get this error when the worker initializes:
Uncaught ReferenceError: document is not defined
    at subs.cljs:729
    at worker.js:1542
the offending line in subs.cljs being this one:
(def canvas (js/document.createElement "canvas"))
So my assumption is that re-frame-10x tries to initialize a canvas in the worker and errors out. My question is, how should I configure this so that re-frame-10x only loads in the :main module?

Stuart Campbell 2021-04-22T02:04:39.267900Z

lol, I don't know why I didn't just try that. Thank you πŸ™‚

kennytilton 2021-04-21T06:17:04.230Z

Nooby Q on re-frame: when a reg-event-fx kicks off multiple dispatches do they each go thru a full six-domino event process? And if there is a :db effect, will that first propagate to all subscriptions before the first event gets dispatched? Most useful would be a pointer to where I can find documented the nitty gritty details of the r/f loop. I am looking at a view manifesting some churning and I need to get a little deeper than the high-level view. Thx!

kennytilton 2021-04-21T06:26:59.231900Z

I thought I would give re-frame-10x a try as a learning tool and bumped it from 0.6.0 to 1.0.2. Now seeing ` ------ WARNING #1 - :fn-arity -------------------------------------------------- Resource: day8/reagent/impl/component.cljs:46:21 -------------------------------------------------------------------------------- 43 | {}) 44 | :operation (operation-name c)}) 45 | (if util/non-reactive 46 | (component/do-render c compiler) ---------------------------^---------------------------------------------------- Wrong number of args (2) passed to reagent.impl.component/do-render -------------------------------------------------------------------------------- 47 | (let [^clj rat (gobj/get c "cljsRatom") 48 | _ (batch/mark-rendered c) 49 | res (if (nil? rat) 50 | (ratom/run-in-reaction #(component/do-render c compiler) c "cljsRatom" when shadow-cljs starts up. Problem? Thx! πŸ™

thheller 2021-04-21T06:30:04.232Z

move the :preloads into the :main module so they are not loaded by the worker.

thheller 2021-04-21T06:30:27.232200Z

just :modules -> :main -> :preloads

superstructor 2021-04-21T06:34:56.232500Z

What version of reagent are you using ? @hiskennyness

superstructor 2021-04-21T06:35:41.232700Z

10x is pretty tightly bound to the internal/private APIs of reagent atm. So, 10x 1.0.2 only works with reagent 1.0.x for example.

kennytilton 2021-04-21T06:44:12.232900Z

Thx! Checking....reagent/1.0.0. I just added a re-frame/1.2.0 dependency as well. Warning is gone. Thx again!

clyfe 2021-04-21T07:28:12.235300Z

I believe I saw some re-frame docs a while back on doing fork-join of sorts. That is: dispatch two events and after both are handled, do a third. It can be done by each handler checking state to see if the other finished first. But I can't find that docs bit anymore. Does anyone know it?

paulbutcher 2021-04-21T08:11:15.235400Z

Thanks!

2021-04-21T08:27:23.235800Z

re-frame-async-flow @claudius.nicolae

2❀️
emccue 2021-04-21T14:26:18.236500Z

@claudius.nicolae Unless it is truly a one time bootup sequence thing, I would avoid async flow for it

emccue 2021-04-21T14:26:40.237Z

lets say they click a button, firing event A, which sends off two http requests

emccue 2021-04-21T14:27:01.237500Z

if they come back successfully they will fire B and C respectively

emccue 2021-04-21T14:27:10.237800Z

and once you have all the data you can do the logic in D

emccue 2021-04-21T14:27:54.238600Z

async flow only runs once, so if they click the button again unless you hack it (we've done it) you don't get the same control flow again

emccue 2021-04-21T14:28:43.239400Z

and I say http requests partially to get you thinkin about what happens if one fails

emccue 2021-04-21T14:28:56.239800Z

your "fork join" logic can't be retryed

emccue 2021-04-21T14:30:32.240400Z

I would either flatten your events or start maintaining the state of what is going on explicitly

emccue 2021-04-21T14:41:04.250600Z

(defn request-dataset-a [db]
  {:db (update db ::model/page-state
               assoc :dataset-a {:status :loading})
   :fx (fx/in-order ;; concat
         (http-fx/request ... 
                          :on-success #(rf/dispatch (success-dataset-a %))
                          :on-failure #(rf/dispatch (failure-dataset-a %))})

(defn request-dataset-b [db]
   ... same as above, replace a with b ...)

(defn both-done? [page-state]
  (and (= :success (:status (:dataset-a page-state))))
       (= :success (:status (:dataset-b page-state)))))

(defn when-both-done [{:keys [db fx]}]
  {:db db
   :fx (fx/in-order
          (alert-fx/alert "DONE!")
          fx)})
  
(defn handler:user-clicked-button 
  [{:keys [db]} _]
  (let [process (compose-effect-fns request-dataset-a
                                    request-dataset-b)
    db))

(defn handler:success-dataset-a
  [{:keys [db]} [_ data-a]
  (let [page-state (::model/page-state db)
        with-data  (assoc page-state :dataset-a {:status :success
                                                 :data   data-a})
        res        {:db (assoc db ::model/page-state with-data)
                    :fx []}]
    (if (both-done? with-data)
      (when-both-done res)
      res)))           

emccue 2021-04-21T14:41:12.250800Z

if that makes sense

emccue 2021-04-21T14:41:43.251400Z

the stuff that isn't handler: is kinda sloppy, i'll admit, but the general idea holds

2021-04-21T16:36:41.253100Z

Question: Is there a way to run a side effecting code when a subscription changed, such that rendering is not blocked by subscription evaluation?

2021-04-21T16:40:02.255Z

another question: when a new return value of a subscription is the same as a previous one, does re-frame caches a new value or keeps an old one?

p-himik 2021-04-21T16:42:04.255100Z

Yes, via dispatch called from reg-sub-raw. But use it only if you absolutely must do that from a sub, for some reason. More details here: https://day8.github.io/re-frame/Subscribing-To-External-Data/ (notice how the very first section says that the document will be retired, and for a good reason). Another approach is to go through a proper event -> event handler -> effect -> effect handler chain. If there can be multiple events that affect the result of the same sub, you can use a global interceptor.

p-himik 2021-04-21T16:45:35.255400Z

re-frame doesn't cache results, it caches reactions created by subscribe. Reagent does cache reaction results. It calls set! on inner cache regarless of the value, but it calls watchers (including the re-rendering machinery) only when (= state old-state) is false.

kennytilton 2021-04-21T17:37:37.259500Z

Is there a known phenomenon where a view rendering function with multiple subscriptions can run one time and not have the data it needs to show, then get that data in a second invocation after the data loads and render it, but the second rendering does not take effect? ie, the data never shows? btw, if we horse around with js/timeout and delay one event or another the same code runs fine, getting multiple renders still, but the data appearing? Sorry, I know that's a lot! πŸ™‚

Lu 2021-04-22T09:24:19.273Z

How does a global interceptor address the issue? Say there are a total of 15 events and the get request needs to be dispatched from 10 of them. Would you have some conditions in the global interceptor to know whether the event in question is one of those 10 so you can make the get call?

p-himik 2021-04-22T09:28:01.273200Z

Shift your perspective towards views. There's a view that needs some data. The view is displayed only after some subscription returns true. So, you simply want to load that data as soon as the value of that subs is true. For that, you create a global interceptor and run the sub function there, comparing the values between the old and the new app-db values. Of course, ideally the sub function should just use get-in or something just as simple, since the global interceptor will be run on every event. So, when the value changes from false to true, the interceptor just dispatches an event that loads the data. That's it.

1πŸ‘
Lu 2021-04-22T09:29:21.273400Z

Oh yeah! I got it I was thinking the side effect needed to happen in the interceptor that’s why I was confused πŸ™‚

kennytilton 2021-04-21T17:38:59.259600Z

The real mystery ^^^: a render with new data that gets discarded/ignored. Thx!

p-himik 2021-04-21T17:42:41.259800Z

It'd be curious to debug it if you have a minimal reproducible example.

kennytilton 2021-04-21T19:20:08.260Z

I know, right! πŸ™‚ Well, I am seeing spaghetti spaghetti dispatches/subscribes in fairly unidiomatic re-frame, so my first step is to clean that up and see if that cures it. Right now the trigger action dispatches two event-dbs and the rendering logic dispatches an event-fx to do an http-get that chains to an event-db setting state to which the renderer is subscribed. gasp :)

p-himik 2021-04-21T19:23:54.260300Z

> the rendering logic dispatches an event-fx What do you mean exactly by the "rendering logic" here?

Lu 2021-04-21T20:59:21.261Z

Hope that’s not happening from the reagent view 😬

kennytilton 2021-04-21T23:27:07.261200Z

Sorry, @p-himik, my r/f terminology is weak... digdigdig...OK, the "view function", domino 5, the one generating the Hiccup, aka @lucio's worst nightmare. It looks like``` (defn myview [] (let [ ... subscriptions to data] (fn [] (let [_(dispatch [:trigger-data-loads]] <a couple of sub derefs>] [:span "What could go wrong?"]))) I do not do much re-frame, but I have alarms going off. And now I seem to be the resident r/f expert. :)

p-himik 2021-04-21T23:36:58.261400Z

Oh yeah, don't do that. Dispatch that data loading event right where you make the decision to show that view. Use a global interceptor or a common function if there are many such places.

kennytilton 2021-04-21T23:42:55.261600Z

Thx for the confirm! The good news is that the data load serves just this one (modal) view. I am considering an event cascade from http-get to app-db-storage, storing also the "show-modal" flag as true and then have the view watch the data and show-modal flag. Thx again.

1πŸ‘