Fork me on GitHub
#re-frame
<
2016-08-14
>
joshkh11:08:05

I got myself into a race condition pickle... maybe someone can offer advice? I have a reg-event-fx that gets dispatched when my application first loads. Its purpose is to fetch some server side assets that are mandatory for future handlers to do their thing. The initial startup looks like this:

(defn ^:export init []
  (routes/app-routes)
  (re-frame/dispatch-sync [:initialize-db])
  (re-frame/dispatch [:fetch-all-assets])
  (dev-setup)
  (mount-root))

; event-fx for starting the asset fetching process

(reg-event-fx
  :fetch-all-assets
  (fn [{db :db}]
    {:db           (assoc db :fetching-assets? true)
     :fetch-assets {:root ""}}))

; side effect for actually fetching the assets

(reg-fx
  :fetch-assets
  (fn [connection]
    (let [c1 (assets/templates connection)
          c2 (assets/lists connection)
          c3 (assets/model connection)
          c4 (assets/summary-fields connection)
          locations {c1 [:assets :templates]
                     c2 [:assets :lists]
                     c3 [:assets :model]
                     c4 [:assets :summary-fields]}]
      (go-loop [channels [c1 c2 c3 c4]]
        (let [[v p] (alts! channels)]
          (if-not (and (nil? v) (empty? channels))
            (let [remaining (remove #(= % p) channels)]
              (dispatch [:async-assoc (get locations p) v])
              (if-not (empty? remaining)
                (recur remaining)))))))))

; Handler for associng each asset to its appropriate location

(reg-event
  :async-assoc
  (fn [db [_ location-vec val]]
    (assoc-in db location-vec val)))
My problem is as follows: When a user accesses the web app via a route (http://example.com/#/report/obj1234), a handler gets dispatched that expects the assets to be there, but of course they're not yet fully retrieved. I thought about testing app db for the assets in the route handler. If they don't exist then store the event somewhere in app-db. When :fetch-assets finishes it could then dispatch anything that was deferred. But my problem is that fetch-assets has the original copy of app-db right after loading which won't contain any pending events. Is there some sort middleware (interceptor?) that could help me untangle my mess? Is there a way to defer a handler until another handler returns app-db?

joshkh11:08:18

Or, maybe in other words, is there an equivalent to dispatch-sync that can handle side-effects?

mikethompson13:08:05

I'm assuming that when :fetching-assets is true you a twirly, "loading ..." thing for the user. In which case, I'd store the current route (in app-db). And when :fetching-assets? gets set to false (not shown in your code), only then navigate to the route. In effect, don't try to store the event which causes the route change, but instead just store the currently selected route in app-db. Just don't try to actually render that route until :fetching-assets becomes false. Have you seen: https://github.com/Day8/re-frame-async-flow-fx (not a solution, but it is related)

joshkh14:08:20

As usual, thanks for the words of wisdom, @mikethompson. Async-flow-fx fell of my radar and now seems like an excellent time to try it out. In regards to the route storage method you suggested, I'd like to render immediately if the assets are present (since fetch-assets only ever happens once). I'll have to check app-db for the assets flag within the route itself to determine whether or not to store it. Would this be an appropriate time to directly inspect re-frame.db/app-db? https://github.com/Day8/re-frame/wiki/FAQ#5-how-can-i-inspect-app-db

mccraigmccraig15:08:03

@rodeorockstar: i'd dispatch an event to do whatever you need with app-db