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
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. π
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?lol, I don't know why I didn't just try that. Thank you π
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!
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! π
move the :preloads
into the :main
module so they are not loaded by the worker.
just :modules -> :main -> :preloads
What version of reagent are you using ? @hiskennyness
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.
Thx! Checking....reagent/1.0.0. I just added a re-frame/1.2.0 dependency as well. Warning is gone. Thx again!
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?
re-frame-async-flow
@claudius.nicolae
@claudius.nicolae Unless it is truly a one time bootup sequence thing, I would avoid async flow for it
lets say they click a button, firing event A, which sends off two http requests
if they come back successfully they will fire B and C respectively
and once you have all the data you can do the logic in D
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
and I say http requests partially to get you thinkin about what happens if one fails
your "fork join" logic can't be retryed
I would either flatten your events or start maintaining the state of what is going on explicitly
(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)))
if that makes sense
the stuff that isn't handler: is kinda sloppy, i'll admit, but the general idea holds
Question: Is there a way to run a side effecting code when a subscription changed, such that rendering is not blocked by subscription evaluation?
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?
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.
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.
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! π
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?
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.
Oh yeah! I got it I was thinking the side effect needed to happen in the interceptor thatβs why I was confused π
The real mystery ^^^: a render with new data that gets discarded/ignored. Thx!
It'd be curious to debug it if you have a minimal reproducible example.
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 :)
> the rendering logic dispatches an event-fx What do you mean exactly by the "rendering logic" here?
Hope thatβs not happening from the reagent view π¬
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. :)
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.
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.