This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2016-02-29
Channels
- # admin-announcements (6)
- # announcements (1)
- # beginners (1)
- # boot (104)
- # braid-chat (5)
- # cbus (1)
- # cider (2)
- # clojure (147)
- # clojure-japan (1)
- # clojure-poland (1)
- # clojure-russia (31)
- # clojurescript (16)
- # core-async (4)
- # css (2)
- # cursive (14)
- # datomic (40)
- # devcards (5)
- # dirac (100)
- # emacs (5)
- # funcool (1)
- # immutant (52)
- # juxt (4)
- # ldnclj (128)
- # lein-figwheel (12)
- # leiningen (26)
- # luminus (3)
- # mount (22)
- # off-topic (11)
- # om (144)
- # onyx (2)
- # parinfer (1)
- # proton (7)
- # re-frame (55)
- # reagent (16)
- # slack-help (5)
- # yada (1)
I'm just getting started with re-frame, and after looking at the example applications, I'm curious how to read the app-db atom directly. It looks like it's not necessary—both sample apps initialize it by merging some initial-data
map into the global db
, which is always the first argument to every handler. This is fine! And in fact, I can get to the app-db atom as re-frame.db/app-db
. I'm just not sure if that's what I should actually be doing.
@amacdougall: you really want to be getting app-db data through subscriptions, and changing it through handlers
Yep, that's definitely the approach I'm taking. If the debugging advice is to "take a snapshot of the app db so you can replay actions", I guess I'd write it to a known atom as a side effect of a handler at some point? And more generally, if I want to view the current app-db purely for debugging purposes, re-frame.db/app-db
is as good (or bad) a way as any.
@amacdougall: There's an FAQ entry on the debugging side of this: https://github.com/Day8/re-frame/wiki/FAQ#5-how-can-i-inspect-app-db I mention it for completeness, because it seems you have already mostly answered that question yourself.
There is also the not-documented-very-well undo
/ redo
side of things, which causes snapshots to be taken. It may act as inspiration.
@nidu gotcha. I can see what you mean.
Sorry, I didn't grok your question correctly. In my mind the general pattern here is:
(rf/register-sub
:item
(fn [db _ [id]]
(let [token (cause-database-query-to-happen :on-success #(dispatch [:write-to [:some :path])])]
(make-reaction
(fn [] (get-in @db [:some :path]))
:on-dispose #(do (cause-database-query-termination token)
(rf/dispatch [:cleanup [:some :path]))))
In this pseudo code, I'm following this pattern:
1. We issue a query and organize that the async results are placed into app-db
at some location.
2. The subscription returns a reaction to that location
3. Via the on-dispose
you cleanup:
3.1 anything about the query itself (which might be a long lasting subscription in rethinkdb, or a one off.
3.2 any data now accumulated in app-db
I think i got it. In my snippet database operations are inside :items/sub
and :items/unsub
handlers so you could be implicit if there's would be a need to.
There are variation on all of this, of course, but you have nailed the salient points in your code.
I really should write this up in a Wiki page
Thanks for being so persistent, and I'm sorry I wasn;t more help oiginally
Remember that you can mix this pattern with dynamic susbscriptions <-------
No worries, that's totally understood. I just found this approach quite elegant and less verbose to track my server sub/unsub needs. Moreover to justify calling dispatchers from subscriptions i can say that data fetching is not actually an action but a workaround for a fact that your data is not in your client app-db
yet (just some words for me to sleep better).
Oh, yeah. I copied and pasted your code, when creating the pseudo code generalisation, and didn't notice the dynamic subscription
And, yes, I regard the arrival of query results to be "an external event".
And hence I like it to be "handled" in an event handler
BUT, I don't necessarily regard the issuing of the query as an event
But this is a very subtle distinction
And I'm not at all confident I'm right
Moreover i thought that this approach can be applied for traditional data fetching. For example fetching can be called with every new subscription (with some debouncing) though that sounds much more doubtful.
Yeah, that's why in initial snippet i just call :items/sub
which itself handles data fetching and calling receiving event.
Yep, understand. My approach doesn't do that. Which is why I called out the subtle difference
BTW, this pattern has some very nice other subtle properties when dealing with databases like rethinkdb which provide the notion of change feeds.
You issue a query ... and then get many updates over time. So you are slowly accumulating state over time.
But equally, you need a way to "stop" the query. When the subscription is no longer needed
Sure. Unfortunately we don't have RethinkDB and we handle subscription mechanism in application logic, but that's not of client side interest
The other aspect is that you can "tag" queries/susbcriptions. And, then,when you issue a mutating query back to the database, you can tell all queries tagged "x" that they should be rerun, to produce new values.
Here with :on-dispose
stopping comes natural which i like. No need to make :component-did-mount
and :component-will-receive-props
for each smart component.
Yep, exactly
The subscription looks after itself
The view is oblivious as to the source of the data
For my task (though it looks quite general to me) there's also generic handler behaviour. There's a counter for each resource identifier (e.g. :items/fetch
or :items/fetch-by-foo
) and argument list (thanks to clojure equality here it's very simple), which is stored in app-db
or elsewhere. Counter is incremented on every sub
call and decremented on every unsub
call. On first increment (from 0 to 1) server subscription occurs and on last decrement (from 1 to 0) server unsubscription occurs. Shortly it's a simple reference counter.
I think that approach will serve you well
One danger ...
If you are using the undo framework, it "undoes" and "redoes" the ENTIRE app-db
Which is not quite right if it contains query results
Solution: 1. Don't use undo / redo 2. Wait for me to add a patch to re-frame to allow partial redo / undo (excluding certain paths, or only including certain paths)
There's one issue i found so far. Initially i stated that this approach allows easy resource tracking: you can unload resource once it has no corresponding subscriptions anymore. But in my case (don't know about the other once) resource usually have at least two overlapping subscriptions, e.g. :items/fetch
and :some-item-parent/fetch-items
which totally discards my idea.
You could just pass the items from the parent to the children as props
And not resubscribe to each item in the children
(I think I'm understanding the issue)
Didn't think about undo. Maybe another solution is to store subscriptions in a separate atom? Ideally this atom can be defined with defonce
and doesn't need any special treatment as subscriptions will discard themselves but probably it brings some complexities as well.
No, there's no subscription for each children. Imagine some simple master detail. We wanna see a list of items (common info) and one specific item (detailed info). Thus we have to subscriptions, e.g. :items/fetch-by-parent
and :items/fetch
. Subscribing each item individually would be painful anyway because you also have to track new/deleted items.
I think re-frame 0.7.0-alpha is supposed to work with reagent 0.6-alpha, but I haven't tried.
hi all, I’m trying to use react-bootstrap with adapt-react-class, and I’ve got a component that expects a function to the onClick prop. I’m passing #(dispatch …) as per the re-frame docs, but chrome complains that it’s getting a string rather than a function. The debugger shows that what it gets is, indeed, a string with a single hash: “#” Any ideas of what might be going on?
@mikethompson: Thanks for the advice! And more generally, thanks for being so active and helpful on this channel.
@tord I'd be interested to hear how you go with 0.6 of Reagent. Although I made the changes necessary to get re-frame working with that version, I haven't ever done much testing.
@hjrnunes: we can't help without a code fragment to look at.
Although you'd probably be better posting to the reagent channel. I've got a feeling this isn't re-frame specific
@amacdougall: my pleasure