Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.Sign up
This document is OLD and contains outdated information. Instead, please see the modern FAQs within the repo
1. Why Can't I access Subscriptions From Event Handlers?
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!!!
You should think about a subscription handler as having two parts:
- A query function
db -> val.
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).
(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)))
my-query is available for use by event handlers, free of the
And, yes, come the revolution, I'm sure we'll be the first ones against the wall. :-)
2. Why Can't I Call dispatch-sync In An Event Handler?
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?
As a general rule, you should always use
dispatch. Only use
dispatch-syncif you specifically need it but, as this FAQ explains, never try to in an event handler.
dispatch-sync are identical in intent, but they differ in terms of when the event's handler is run:
dispatchqueues the event for handling "later"
dispatch-syncruns 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:
dispatchan event - it will be handled sometime AFTER the current handler completes.
dispatch-syncan 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:
and then, afterwards, inspect
app-db we'd see:
:bwith a value of
- no change
:a- surprisingly it doesn't have the value
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
- event handler for [:b] returns modified db which is put into
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.
3. How can I denormalise data within a re-frame application?
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?
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.