As I said in the comment, this is rarely an issue with re-frame in my experience.


@p-himik Hooks come with certain rules, like they must be called in exactly the same order each time. And there can't be any conditional logic which might mean they are "in" for one render but not the next. Also, IMO resist the temptation to just embrace everything React/javascript, or else you'll find yourself swimming in the messy, washing machine churn that is javascript. Uggh. I think it is well worth trying to find the ClojureScript expression of ideas.


2014 React was a masterpiece. (And Reagent was a brilliant insight in addition to that. Thank you Dan!). What's happened to React since then ... not so impressive. In fact, it looks like a return to the previous messy churn of javascript, stumbling forward, one unsatisfactory solution after the next, always heralded with breathlessness, and then discarded two years later. IMO.


Hooks have been complected deep into the rendering machinery for only one reason: so enable the "suspense" feature. Which has greatly increased the complexity and hasn't even been delivered yet because of "reasons". And even when it is delivered, it can only be shown off in artificial benchmarks involving "Sierpinski Triangles". Hmm. I am exaggerating here to make a point. Way better, in my opinion, if they made React better at using shared workers (logic in one thread, render in another. cleanly separated, and not horribly intertwined). The "Sierpinski Triangles" demo of this arrangement would look just as good, if not better. Anyway, end of rant. (I reserve the right to completely change my mind later :-))

Oliver George 2020-08-10T03:40:38.289200Z

Idle thought of the day. I think re-frame handlers would be more composable if they returned two keys :db and :fx where :fx is a collection.

Oliver George 2020-08-10T03:40:40.289400Z

:db needs to be threaded through as each handler modifies the app-db in turn

Oliver George 2020-08-10T03:41:27.290400Z

:fx would allow all side effects to be kept in a list and executed in turn. No chance of clobbering each other.

Oliver George 2020-08-10T03:41:51.291Z

e.g {:fx [{:dispatch ...}{:dispatch ...}{:dispatch ...}{:dispatch ...}]

Oliver George 2020-08-10T03:43:04.292300Z

So to compose you thread :db through handlers and collect up all :fxs returned.

Oliver George 2020-08-10T03:43:23.292700Z

Expecting answers such as: been there done that, you're a monster, shouldn't you use more interceptors...


@olivergeorge It is a good idle thought. I have made this comment elsewhere: if I had my time over again, I would absolutely make effects be a seq, rather than a map


Upside: 1. It gives ordering (not that useful, but an interesting property) 2. no need for dispatch-n as you point out (this same -n issues comes up a lot with other effects like http GETs etc Downside: 1. slightly nosier visually with the additional structural nesting. 2. breaking change


So I'm interested in your idea because it is a way to take away Downside 2, maybe.


@olivergeorge ^^


In terms of ordering, if I return both effects {:db ... :fx [....]} which effect is actioned first? :db ?


What if we put dB inside fx?

Oliver George 2020-08-10T06:12:47.300Z

I think :db is a special case. it's changing state... not triggering side effects. In fact, it's important to keep it out if you want to compose a bunch of simple handlers together which I think would be really nice and avoid a lot of typical boilerplate in handlers.

Oliver George 2020-08-10T06:13:32.300400Z

There can be only one

Oliver George 2020-08-10T06:16:03.302200Z

I guess there are cases where multiple fx would conflict (excepting :db) but none come to mind.

Oliver George 2020-08-10T06:18:24.303200Z

I suspect it doesn't matter if :fx or :db is actioned first since they are independent... fx never have access to current app-db state.

Oliver George 2020-08-10T06:19:04.303800Z

So long as all fx are triggered and the :db is updated before any other events are processed then it's fine. (In reflection, I'd do the :db first to allow for :fx which act on the app-db atom directly. Not something to encourage but logically what you'd expect - state has changed and after effects play out)

Oliver George 2020-08-10T06:20:41.305300Z

I suspect the {:db ... :fx []} can be handled by re-frame v1 via an interceptor. Sounds fiddly but can't see why it wouldn't work.

Oliver George 2020-08-10T06:21:56.306500Z

Could even be a new type of event handler registration which handles the return value differently: re-frame.core/reg-event-fsm (or whatever)

Oliver George 2020-08-10T06:22:04.306800Z

(so non-breaking addition to reframe)

p-himik 2020-08-10T07:40:48.307400Z

> it looks like a return to the previous messy churn of javascript I feel more or less the same way and that's why I don't really try to learn the new stuff unless it seem to be vital. So I don't really know that much about hooks. :) But some of the Clojurians members find them a tremendous addition that solves many problems. /shrug/

Grant Isom 2020-08-10T15:18:30.308200Z

@mikethompson I want to instead of queue a http-xhrio event, call a mock response function but struggling how to structure the handler in a straightforward way.

benny 2020-08-10T19:04:43.310900Z

probable noob question, but why is it that when i dereference a subscription inline like so

(let [my-data @(re-frame/subscribe [:my-sub])]
  (fn [] 
    [:div my-data]))
the data doesn’t refresh but it does when dereferenced like so
(let [my-data (re-frame/subscribe [:my-sub])]
  (fn [] 
    [:div @my-data]))
even though the todo example uses the inline approach? (


If fn is your component, then by doing it in the outer let you’re not deref’ng during the render phase. Reagent afaik will only track derefs that happen during render.


Right, and that can be a handy tool. For example, you want to populate a dropdown menu with values you get from an ajax call, but you don’t want to re-run the ajax call every time the component renders. So deref in the let bindings, and it will only happen once, when the component is mounted.

benny 2020-08-10T19:10:14.312700Z

but i just tried it with the deref in the let bindings and it picked up a value now by removing my inner component

benny 2020-08-10T19:10:41.312900Z

so now i have

(let [my-data @(re-frame/subscribe [:my-sub])]
  [:div my-data])
and it picks up new data after initial render

benny 2020-08-10T19:11:29.313100Z

seems like these two accomplish the same thing, but i’m not sure i understand the consequence to each

(let [my-data @(re-frame/subscribe [:my-sub])]
  [:div my-data])
(let [my-data (re-frame/subscribe [:my-sub])]
  (fn [] 
    [:div @my-data]))


Does it re-render with new data if the data behind :my-sub changes? That would surprise me.


what’s the whole component look like? you might be unintentionally creating a type 2 component.


If there’s a let binding outside the render function, it’s definitely a form-2 component.


^ IE the fn inside the let is what is different about your two examples

benny 2020-08-10T19:15:07.314600Z

it’s gigantic, but a watered down version would be like this:

(defn home []
  (let [alert (re-frame/subscribe [:alert])]
    (fn []
      [:div {:style home}
        [:div {:style main} /* home content*/]
        [:div {:style modal} @alert])))


I’m not at all sure about this, but it’s possible that if you deref the subscription inside the let binding, it will re-run your render function when the data behind [:my-sub] changes. The problem is that it will just re-render my-data, and that’s going to remain at whatever value came out of [:my-sub] the first time you de-referenced it.

benny 2020-08-10T19:15:45.315Z

i would prefer not to re-render the entire home component and just display a modal. my guess based on what you guys are saying is i want to deref in my let?


I would make the entire [:div {:style modal} @alert] bit its own component--that’s the best way to avoid unnecessary re-rendering in the parent component, plus it makes it simpler to keep track of the subscriptions you need. Well, usually it does anyway.

benny 2020-08-10T19:19:15.315400Z

very true, that makes sense, thanks guys

Oliver George 2020-08-10T23:14:03.316500Z

Thanks @mikethompson I'll look forward to seeing what others think about it. I've done my best to clarify my thoughts in a comment.