in a reg-event-*
fn, if i need a value from the db, is it better practice to subscribe to it or access it directly from the db co-fx?
Use subscribe
only in views and in signal functions of other subscriptions (maybe also in sub bodies and in reg-sub-raw
). Don't use it in event handlers.
So yes, just access the right value in the cofx map.
gotcha thanks
when i'm using the db and another source for my subscription, is it normal that i provide a subscription to the entire db myself? the db
param to reg-sub
doesn't seem to be provided if i'm providing a subscription to derive the value from. for example, is this typical?
(rf/reg-sub
:db
(fn [db _]
db))
(rf/reg-sub
:some-id
:<- [:db]
:<- [:kee-frame/route]
(fn [[db route]]
@lucian303 You'll want to understand the difference between Layer 2 and Layer 3 subscriptions http://day8.github.io/re-frame/subscriptions/
thanks @mikethompson i see why that was a bad idea and it was easily fixable
See them in action, with further explanation, here within the todomvc example: https://github.com/day8/re-frame/blob/master/examples/todomvc/src/todomvc/subs.cljs
are there any example apps/guides that sets up websockets in a re-frame app?
I don't know of any examples but I use it as any other effect, although in this case it is stateful. I'm using Sente and I have two effects - one to create the connection and one to send messages. That's it. I don't think there should be anything more to it.
that make sense, I was already guessing that messages should be events/effects, but didn’t think of treating connection management as such too, thanks for the suggestion
Hi, just curious, is anyone using subscriptions as input to events? Eg. via the inject cofx provided by https://github.com/den1k/re-frame-utils that is referred to in https://github.com/day8/re-frame/issues/255. The reason I ask, is the I very often end up in the situation where I have some derived and usually heavily processed data available in a subscription, but need it in an event as well. In general I feel that it would be much cleaner to not have to work around it by re-processing, write derived data back to db or route it via UI code. I believe it would also enable me to minimize the number of locations that knows about where in DB a given piece of source data lives. PS: In relation to issue 255 my usecase is bullet 1), using data in a subscription, not triggering an event when sub data changes. I would also like to use subscriptions that is not in use by UI code, but might well be “sibling” nodes in the subscription graph. I’m aware that there is a performance penalty there, but I kind of assume that would be ok from a usability point-of-view for less frequent events. Thought I’d just ask before I start experimenting with a 4 year old add-on…
This question gets asked once or twice a month, so yeah, there are people that do that. :)
OK, thanks. If we assume that they don’t just ask the question, then hopefully some see this and can share some experiences 🙂
We are using the pattern in a couple of places as well, and while it seems easy to reuse subscriptions as data extractors outside of "view" code, I also think that depending on subscriptions requires understanding the context in which that code runs. Basically whatever depends on a subscription is no longer a pure code and thus all common consequences.
We tend to reuse pure function between handlers and subscriptions instead.
Event handlers can be pure - the aforementioned re-frame-utils
just injects a cofx. You don't subscribe
anywhere explicitly.
It's also super tempting to reuse a cached value from a subscription instead of calculating one from scratch, especially when calculation happens frequently.
To me this topic still remains an unanswered question in re-frame
Thanks, @roman01la I see what you mean. While the handler can still be pure, as @p-himik says, the overall situation is less “pure”
The downside is that if that sub is not used in any view, it will be computed on each event dispatch. A teeny bit slower even, because of the cache and the reaction machinery involved. IIRC there's a flag to leave such a sub in a cache even if it's not used by a view, so at least there's a remedy.
> The downside is that if that sub is not used in any view, it will be computed on each event dispatch We are working around that by maintaining a pool of such subscriptions, so that they don't get GCed
But what exactly might “slower” mean here? 100ms? 1s?
@roman01la Why a pool, given that there's a way to do that via re-frame-utils
via the :ignore-dispose
metadata?
To me, since re-frame is essentially a memoized computation graph it does make sense to use it for generic computations, maybe it's that re-frame's API is tailored towards state management in UIs
Re: performance, this is relevant, I think? https://github.com/day8/re-frame/issues/657
@p-himik I'm not aware of that option, can't really tell why
@mokr I've never measured but I'm pretty sure that caching and reagent machinery won't slow your events to a noticeable degree. Otherwise, all your subs would've been slow already. The absence of caching or the absence of GC on the other hand is a real issue where you have to make a conscious decision on what you want more.
It should be noted that unless that issue is fixed, that's an 2^n performance issue if you have derived subs based on derived subs -- it can make a difference.
@timothysdean That particular issue should not affect performance. It's just a deref.
Absence of caching sound ok, but absence of GC? That sound like a leak, I I thought the penalty was partly due to it avoiding leaks?
Absent caching the deref has a cost, right?
No. re-frame-utils
disposes of a sub explicitly. By itself, sub cache is never pruned.
Today my actual usecase was a situation where the user has filtered down a number of nodes to a selection that should be sent to backend for processing. So, a one-off thing really.
In regular re-frame apps, the cache is cleared of a particular sub when that sub is no longer used in any reactive context (a view, as a signal sub, being deref'ed in a reaction).
Since event handling happens outside of a reactive context, by themselves any subs there won't be removed from the cache (that's also the reason for why you should never call subscribe
in a JS event handler, for example). And that's why re-frame-utils
disposes of such subs manually, unless the appropriate metadata is set.
Are you referring to the :ignore-dispose metadata of re-frame-utils? The way I read it that is only disabling the warning.
Oh, you're correct! I was wrong about it. Now I see why @roman01la had to have a pool of subs.
So, all in all if I decide to use this inject, and without a pool, it is safe, but I pay a performance penalty? I’m not messing up re-frame in some other more severe way affecting other part of the app?
Right. But you don't "mess up" re-frame in any other way anyway. It's just a tradeoff between performance and having a memory leak (and depending on how that pool is implemented, even the leak can be mitigated).
Thanks for all the input. With that in mind I think I’ll have a go and see where it takes me.
I refer to @mokr's question immediately above and to the answers provided by @p-himik and @roman01la...
I'd like help from you guys in writing a good problem definition (the best gift you can give yourself when solving a problem is a good problem definition). And that's because (as @p-himik pointed out this come up a bit too often and we need a solution). I'll turn this into an issue.
My first cut of a problem definition in note form is:
• within a re-frame app ...
• ... for a certain period of time (perhaps while a certain panel is showing) ...
• ... there's a n need to compute a materialised view of data within app-db
...
• ... which will be used both within views (provided by subscriptions) ...
• ... and within event handlers.
• The computation involved may be a CPU expensive, so we want to cache it. Ie. we don't want to have it calculated via subscriptions and then ALSO calculated within event handlers. We want it only calculated once, when required and as inputs to the calculation change, and then available to both contexts.
• BUT we also don't want it calculated outside of the "time" when it is needed. For example, the calculation might only be needed when a single panel is showing to the user.
Just to repeat. I'm more interested in a crystal clear problem definition at this point, and less about solutions (although good ideas are welcome).
Actually, I created an issue to collect information https://github.com/day8/re-frame/issues/680
The problem statement is crystal clear to me, thanks 👍