Fork me on GitHub
#re-frame
<
2022-03-11
>
Kimo01:03:05

Hi #re-frame, Is it okay to call dispatch-sync in a coeffect handler?

p-himik11:03:18

No. But you can use a different interceptor. And instead of dispatch-sync it would be better to just alter the coeffects/effects maps directly.

Kimo12:03:10

Interesting, thanks.

👍 1
Kimo12:03:53

Trying to come up with a pattern for dependent resources. For example:

Interceptors A and B do API requests. B "depends" on A.
If B finds that A is undone, B pauses and dispatches an event with A.  
    (e.g. (when (-> context :coeffects :db :results :A nil?) (pause-and-somehow-do A)))
When A succeeds, resume B with the rest of its queue.                  
    (e.g. {:http-xhrio {:on-success [:somehow-resume context-B]}})
Does anything exist for this? I can see how it might work, but the async pattern of requests throws me off. Seems like multiple events need to be dispatched, but not sure how to thread a description of pausing/resuming/giving-up through the events.

p-himik12:03:51

I feel like you're overcomplicating things. Why do you use interceptors for API calls in the first place? Just use the main event handler for that.

p-himik12:03:00

Split the logic into two parts - one to request the required data and the other to use that data. In the event handler, check if the data is there. If not, return an effect map that would fetch it and continue with the usage. And if the data is there, just continue with the usage.

Kimo13:03:41

That makes sense, somewhat. But since an event is an interceptor, it seems like A & B could do anything these events could do. I'm hoping to reduce boilerplate and fragmentation by abstracting the resource life-cycle. So I could easily declare resource requirements for UX events:

(reg-event-fx :land-on-page [A} {})
(reg-event-fx :button-pressed [B] {})
(reg-event-fx :another-button [B] {})

Kimo13:03:40

In this example, if :another-button dispatches, it does B's api request. But first, it does A if necessary. Later when you dispatch :another-button, it doesn't do any requests since the results are already in app-db.

p-himik13:03:49

An event handling happens right on the cusp between switching from :before to :after interceptors. Any other interceptors can't do that without heavily interfering with the interceptor queue. And interceptor queue handling is synchronous. Even if you use dispatch-sync, nothing in there will by itself wait for, say, a network request to finish. An workflow that's split into multiple async events where some fetch the data and some handle it works because every individual event is synchronous - it's their orchestration that's async.

p-himik13:03:45

In other words, the whole event handling procedure is like a sync function call. You can't do a network request in the middle of it.

p-himik13:03:37

The way to simplify workflows and avoid duplication here is to write composable helper functions that you will then use to write actual event handlers.

Kimo13:03:36

I'm starting to imagine such helpers.. Any chance you could write me a pseudocode?

p-himik13:03:17

(defn make-fetch-x-effects [ctx on-response args]
  {:http-xhrio {:method :get
                ...
                :on-response on-response}})

(defn ensure-data-loaded [{:keys [db] :as ctx} {:keys [data-key fetch-data args on-data-ready]}]
  (if (contains? db data-key)
    {:dispatch (conj on-data-ready (data-key db))}
    (fetch-data ctx on-data-ready args)))

(reg-event-fx ::do-stuff
  (fn [ctx [_ & args]]
    (ensure-data-loaded ctx
                        {:data-key      :x
                         :fetch-data    make-fetch-x-effects 
                         :args          args
                         :on-data-ready [::-do-stuff-impl])))

(reg-event-db ::-do-stuff-impl
  (fn [db [_ x]]
    (assoc db :stuff-is-done (inc x))))
Something like that. Just a quick sketch, so lots of things could be improved.

👀 1
👍 1
p-himik13:03:46

As an alternative that's better in some use cases, you can create a custom effect that would be like :http-xhrio but with a local cache.

p-himik13:03:04

If having multiple chained requests is for some reason a common pattern in your app (if you're using a single server, it shouldn't be - your API design should be improved instead), then such an effect could handle that as well.

Kimo13:03:44

Yes I'd rather just remove that pattern, haha. Could be possible, not sure.

Kimo13:03:46

Thank for your effort, it robustly springboards me.

👍 1