day8 / re-frame

A ClojureScript framework for building user interfaces, leveraging React
http://day8.github.io/re-frame/
MIT License
5.4k stars 715 forks source link

Adds function to simplify creating new event types #779

Open Sardtok opened 1 year ago

Sardtok commented 1 year ago

Adds the function reg-event to re-frame.core which takes a handling interceptor instead of a handler as its last argument. This makes it easier to create event registering functions with all standard interceptors added to the chain, even if changes are made to the standard interceptor chain.

reg-event-db, -fx and -ctx now wrap reg-event, passing their interceptor wrapped handlers.

Sardtok commented 1 year ago

This is a suggestion to make creating custom event types a tiny bit easier.

Here's an example use case. Let's say we want to catch exceptions and add exception traces. If we don't want to add the standard chain ourselves (adds maintenance risk, although a low risk as this isn't code that changes often), right now we could write this as:

(defn- interceptor->with-exception-tracing [f]
  (fn [context]
    (try (f context)
         (catch :default e
           (trace/merge-trace! {:tags {:exception e}})
           (throw e)))))

(defn- wrap-exception-interceptor
  [id]
  (let [interceptors (into [] (get-in @registrar/kind->id->handler [:event id]))
        last-interceptor (peek interceptors)
        wrapped-interceptor (update last-interceptor :before interceptor->with-exception-tracing)]
    (swap! registrar/kind->id->handler assoc-in [:event id]
           (conj (pop interceptors) wrapped-interceptor))))

(defn my-reg-event-db
  ([id handler]
   (my-reg-event-db id nil handler))
  ([id interceptors handler]
   (re-frame/reg-event-db id interceptors handler)
   (wrap-exception-interceptor id)))

If we don't mind adding the interceptors ourselves, we could shorten this a bit, but this adds some risk that changes to the standard interceptor chain aren't caught, and the event will act in a non-standard way:

(defn my-reg-event-db
  ([id handler]
   (my-reg-event-db id nil handler))
  ([id interceptors handler]
   (events/register id [cofx/inject-db fx/do-fx inject-global-interceptors interceptors
                        (-> handler
                            std-interceptors/db-handler->interceptor
                            (update :before interceptor->with-exception-tracing))])))

With the suggested edit the final registration is simpler:

(defn my-reg-event-db
  ([id handler]
   (my-reg-event-db id nil handler))
  ([id interceptors handler]
   (re-frame/reg-event id interceptors
                       (-> handler
                           std-interceptors/db-handler->interceptor
                           (update :before interceptor->with-exception-tracing)))))

Might not be a huge difference, but it feels a bit more ergonomic. And if changes are made to the standard interceptor chain, they are only made in one place.