Re-frame's co-effects, what for?

clojurescript

#1

I’ve been evaluating co-effects thingy from re-frame. I think I understand what is its purpose, but not sure how often people actually use it. For me it’s mostly a shortcut for “read” events which also makes such event handlers easier to test.

What are your thoughts on co-effects?


#2

Everytime we use reg-event-db, it’s actually injecting the db co-effect so I think we use it a lot.
Beside this one, I used a co-effect and an effect for getting and setting data into localstorage.

I agree that we need cofx less frequently than fx or other part of the library but it’s very nice to have it.


#3

I’ve used coeffects to create random UUIDs for certain events that need to create copies of data, each with a unique ID.

(rf/reg-cofx
 :new-uuid
 (fn [coeffects _]
   (assoc coeffects :new-uuid (random-uuid))))

#4

I’ve used them to inject the viewport size and scroll position into some basic animation stuff, inspired by this lib.

(rf/reg-cofx :scroll
  (fn [coeffects _]
    (assoc coeffects :scroll (.-y (dom/getDocumentScroll)))))

#6

The viewport size injection sounds interesting. Could you share more on those coeffects? What DOM nodes do you measure on, and how? I’d like to get some basic viewport dimensions into re-frame, mostly for specific DOM nodes.


#7

Re-frame’s co-effects are for providing impure data (like localStorage, DateTime now, random numbers/uuids, DOM attributes, etc.) to your event handlers. The documentation provides good detail about when they should be used. I find they’re most useful for avoiding events that don’t affect state but have side-effects that trigger more events.

One of my coworkers designed a similar middleware for Redux in JavaScript. We use it most often for DateTime now and localStorage, but also for data like auth tokens or parts of the Redux store owned by a different reducer/actions (which is usually accessible anyway in re-frame).


#8

I used component lifecycle and refs for that:

  (let [elem (r/atom nil)]
    (r/create-class
      {:component-did-mount (fn []
                              (let [size (.getBoundingClientRect @elem)]
                                (dispatch [:canvas/resize (.-width size) (.-height size)]))))
       :reagent-render      (fn []
                              [:div {:ref (partial reset! elem)}])}))