-
-
Notifications
You must be signed in to change notification settings - Fork 716
FAQ
This document is OLD and contains outdated information. Instead, please see the modern FAQs within the repo
Question
Those pesky re-frame "rules" say I can only use subscriptions in Components. But, in my event handlers, I need to use the same query. Why can't I use a subscription there too?
I am the Che Guevara of the Reagent world, and I will not be oppressed by their filthy capitalist, pig dog rules!!!
Answer
You should think about a subscription handler as having two parts:
- A query function
db -> val
. - a
reaction
wrapping
The reaction
wrapping delivers "a stream" of updates over time. The means the query will be rerun whenever "app-db" changes, and that's perfect for Components which, in response, might need to rerender
But event handlers don't need that. They need to do a single query, which yields one result, all based off the db
param supplied. They don't need a stream of query results.
So, if you find yourself needing to query db
in your event handlers, and wishing you could use a subscription, you should:
- factor out the reusable query into a function
- within your subscription, use that function, and wrap it in a reaction (to get a stream of values)
- within your event handlers, call the function directly (to get a single value).
Sketch:
(defn my-query
[db v]
.....) ;; return some interesting value based on db
;; a subscription handler
;; needs to produce a "stream" of changes, based on my-query
(register-sub
:some-id
(fn [app-db v]
(reaction (my-query @app-db v)))) ;; use my-query with @app-dp, in reaction
;; an event handler
;; needs to perform the query once, to obtain a value.
(reg-event-db
:h-id
(fn [db v]
(let [calc (my-query db v)] ;; use my-query to get a one off value
.... use calc)))
So now my-query
is available for use by event handlers, free of the reaction
wrapping.
And, yes, come the revolution, I'm sure we'll be the first ones against the wall. :-)
Question
In an event handler, I'm allowed to dispatch
further events. But I'm not allowed to use dispatch-sync
. Why? Aren't they pretty much the same?
Answer
As a general rule, you should always use
dispatch
. Only usedispatch-sync
if you specifically need it but, as this FAQ explains, never try to in an event handler.
dispatch
and dispatch-sync
are identical in intent, but they differ in terms of when the event's handler is run:
-
dispatch
queues the event for handling "later" -
dispatch-sync
runs the associated event handler RIGHT NOW.
This "later" vs "right now" difference is the key.
If we are currently halfway through running one event handler, and we:
-
dispatch
an event - it will be handled sometime AFTER the current handler completes. -
dispatch-sync
an event - it will be handled immediately, before the current handler completes.
To illustrate, assume we have these two simple event handlers:
(reg-event-db
:a
(fn [db _]
(assoc db :a 100)))
(reg-event-db
:b
(fn [db _]
(dispatch-sync [:a]) ;; <-- dispatch-sync used here
(assoc db :b 5)))
If we were to:
(dispatch [:b])
and then, afterwards, inspect app-db
we'd see:
-
:b
with a value of5
- no change
:a
- surprisingly it doesn't have the value100
It is as if (dispatch-sync [:a])
never happened. Its modification to :a
is lost.
Here's why. Because dispatch-sync
is used, the process is:
- event handler for [:b] called with db snapshot
- event handler for [:a] called, with db snapshot
- event handler for [:a] returns modified db which is put into
app-db
- event handler for [:b] returns modified db which is put into
app-db
Step 4 overwrites step 3, which means that step 2 is lost.
re-frame detects nested handler calls and will produce a warning if it occurs. This FAQ entry is here mostly to explain why you got that error.
Question
My app-db
is structured like a normalised database and want to "join" parts for display purposes. For example, I have many wibblies and I want to display them in a table, but each wibble has a wobble. How should I do that?
Answer
One way is to use a form-2 component which accepts an id and subscribes to a denormalising subscription based on that id:
(defn my-component[id]
(let [denormalised-state (subscribe [:denormaliser id])]
(fn [id]
[:div (:some-denormalised-state @denormalised-state)])))
See Colin Yates' exploratory repo here for more info.
Deprecated Tutorials:
Reagent: