Implement a sequential dispatch pattern in re-frame #746
Replies: 1 comment
-
Will you clarify a little? It sounds like you're trying to do a few things:
For the first part, it sounds like you're dispatching from within a reg-sub? Something like this: (reg-sub
:get-data
(fn [db [_ path]]
(or (get-in db path)
(do (dispatch [:fetch-data path])
nil))) This approach feels declarative: return my data, or fetch it if you need to. But it means your subscription is impure, which doesn't fit with re-frame's model. You can't control when and how often subscriptions fire. So, depending on how you've structured your views it's possible that the same request will be dispatched several times. For example, imagine if two components subscribe to the same Usually the answer is to separate the events and the subscriptions. If you want to avoid re-requesting data that may have been fetched before, do something like this: (reg-event-fx
:fetch-data
(fn [_ [_ path]]
{:http-xhrio (request-for-path path)}))
(reg-event-fx
:ensure-data
(fn [{:keys [db]} [_ path]]
(when-not (get-in db path)
{:dispatch [:fetch-data path]})))
(reg-sub
:get-data
(fn [db [_ path]]
(get-in db path))) Then, dispatch If you want to bring the fetch closer to the subscription, you can try (defn view [path]
(with-let [_ (dispatch [:ensure-data path])
data (subscribe [:get-data path])]
[:div (pr-str @data)])) This usually works, but I'm always wary of it. It's not easy to reason about when views will mount or unmount. I feel safer being explicit, dispatching in response to some intentional event (button click, page load) instead of an implied event (view mount). I admit, it's less declarative, but re-frame is unlikely to change to permit db mutation within subscriptions. The second need—ensuring that only one request is in flight at once—has always been tricky in re-frame. It sounds like you want a "request manager": a state machine that contains a request queue. You ask it to enqueue requests and it decides what to do:
For modeling state machines in re-frame, there's https://github.com/Day8/re-frame-async-flow-fx, but I think that https://github.com/lucywang000/clj-statecharts has more potential. You may get some ideas from https://github.com/ingesolvoll/glimt, which uses clj-statecharts to manage http requests in re-frame, though in a slightly different way than you're describing. |
Beta Was this translation helpful? Give feedback.
-
I'll explain the pattern we have been implementing for now:
@(subscribe [:get-data "idA" [:adress [:street])]
We want that pattern to be sequential, that is, that the "query in progress" marker is always written to the app-db and propagated to the signal graph before another
:get-data
subscription dispatches a new http fx query.e.g:
Here, the second suscription cannot execute before the first subscription dispatched the "query in progress" marker, and before the changes in the app-db have been propagated to the signal graph.
As of now, no combination of
dispatch-sync
andreagent/flush
was enough to make that pattern sufficiently sequential. Although those helped, the pattern doesn't seem to be truly deterministic, as some queries still sometimes run in the same time.Beta Was this translation helpful? Give feedback.
All reactions