Fork me on GitHub
#re-frame
<
2021-12-08
>
lambder10:12:33

hello devs, I'm trying to write an interceptor which upon ajax-failure event will analyse the event. If the event is of unauthenticated nature. I'd like to erase the ajax-failure event and replace it (modify the context somehow) with dedicated ::unauthenticated event. This way I could have common behaviour to deal with it. What is the best way to do it?

lambder10:12:05

(re-frame.core/->interceptor
    :id :detect-unauthorized

    :before (fn [context]
              (let [coeffects (:coeffects context)
                    initial   {:db    (:db coeffects)
                               :event (:event coeffects)
                               :fx    []}
                    result    (cond-> initial
                                      (-> coeffects :event first #{::failure-domain-result})
                                      (#(do
                                          (prn ":detect-unauthorized-before")
                                          (-> %
                                              (assoc :dispatch [::unauthorized])))))

                    _         (prn result)
                    effects   (select-keys result [:db :fx])]
                (assoc context :effects effects)))

lambder10:12:47

but can still the ::failure-domain-result event on the re-frisk

p-himik10:12:12

I don't think it's a good use of an interceptor. For such behavior, I would definitely write a custom effect that wraps the AJAX effect you're already using.

lambder10:12:17

understood.

lambder10:12:33

now more general question. forget all the ajax thing.

lambder10:12:32

how to write a mechanism (via interceptor or other) which upon detection of some event (potentially using its params) erase that event and emit an alternative one?

p-himik10:12:51

A global interceptor with :before.

lambder10:12:06

what do i do in that interceptor?

lambder11:12:53

for those interested. This works:

lambder11:12:00

(def detect-unauthorized
  (re-frame.core/->interceptor
    :id :detect-unauthorized

    :after (fn [context]
             (if (-> context :coeffects :event first #{::failure-domain-result})
               (assoc-in context [:effects :dispatch] [::unauthorized])
               context))))

(re-frame.core/reg-global-interceptor detect-unauthorized)

lambder11:12:21

@U2FRKM4TW thank you for hints

p-himik11:12:56

Well, that only works for events that are dispatched via the :dispatch effect. However, there are events that are dispatched using dispatch, dispatch-sync, :dispatch-n, :fx, maybe even potentially modifying the queue directly (don't do that of course). A global interceptor with :before would handle all such cases simply because it works right on top of the events that are about to be handled, and you can dynamically replace that handling.

p-himik11:12:12

I'll try to come up with a small example.

lambder11:12:03

there is still small problem

lambder11:12:35

i got this event registration: (kee frame)

lambder11:12:37

(rf/reg-event-fx ::unauthorized
                 (fn [_]
                   {:navigate-to [:logout]}))

lambder11:12:36

so the global interceptor correctly dispatches the ::unauthorized when it gets unauth and the navigation correctly navigates to :logout route

lambder11:12:12

the ::failure-domain-result event happens anyway and the :app-db gets updated unfortunately

lambder11:12:33

now I just need to erase the ::failure-domain-result event somehow

p-himik11:12:07

Don't do that - it's a vain task given how it won't address all the rest of the issues.

lambder11:12:09

I appreciate the good advice. Which I agree with. But I still want to understand how it's done.

lambder11:12:40

since context is just a map, I'd like to understand how the event dispatching mechanism still works after I've updated the context.

p-himik11:12:36

There's no one to better answer that question better than the re-frame source code itself. :)

lambder12:12:00

@U2FRKM4TW after reading the source code and much thinking, I believe messing with events is bad idea. Basically it's not clean.

lambder12:12:33

Instead I use https://github.com/JulianBirch/cljs-ajax/blob/master/docs/interceptors.md (different kind of interceptors) to handle the unauth case.

p-himik12:12:01

Not sure what you mean by "not clean" in this case. Altering the interceptor chain of a particular event doesn't result in any side-effects or loss of referential transparency.

p-himik12:12:27

But if the library that you're using can already handle your use-case, then it's definitely the way to do. I was assuming there was no such functionality.

p-himik12:12:55

Also, I promised to come up with an example of such a global interceptor that replace one event with the other, but if you don't really need it I won't do that because writing it requires some careful thinking and most likely using private re-frame API.

lambder12:12:41

by not clean I mean is against the re-frame nature.

lambder12:12:23

look, the event dispatch is not just mutating the context, but it enqueues the event object via router to the queue

lambder12:12:43

so manipulating the context can't emulate the event not happening.

p-himik12:12:10

> against the re-frame nature I wouldn't say so, it's even documented to some extent: https://github.com/day8/re-frame/blob/master/docs/Interceptors.md#self-modifying > the event dispatch is not just mutating the context It's not mutating, it's creating a brand new context. Like every single interceptor, even the built-in path. All according to the nature of immutable data - all is fine here. > enqueues the event object via router to the queue Not in what I had in mind - it would simply modify the documented :queue value of the context, that's it.

p-himik12:12:23

> so manipulating the context can't emulate the event not happening. This is a false statement, but with an asterisk. You can replace the interceptor that actually invokes the event handler function with a no-op, along with all the other interceptors that particular event handler registration uses. But you can't make it seem as if the event was never in the queue in the first place. Fortunately, not a lot of things care about that.

p-himik12:12:42

Just checked - pretty sure that, unless a library uses private re-frame API, nothing but re-frame.core/add-post-event-callback cares about the real event. So yes, there's a caveat, but a known and a rare one.