clyfe 2020-12-16T01:04:46.457100Z

Reporting on initial POC there: I got subscribe + dispatch-sync working; dispatch (plain / async) looses the frame context on next tick. I need to somehow glue the frame with the event in the FSM across ticks.

lilactown 2020-12-16T01:21:07.457600Z

the issue is that you're using binding / with-redefs. you should just use context for everything

lilactown 2020-12-16T01:23:25.457800Z

well, let me be a bit more specific. the issue is that you're not closing over the bound values; you need to bind them lexically and then refer to them in the closure

lilactown 2020-12-16T01:25:14.458100Z

I misread, you are closing over the frame lexically

lilactown 2020-12-16T01:27:05.458300Z

CLJS doesn't have binding-conveyance like Clojure so you are probably out of luck w/ maintaining the redef'd frame across the async barrier 😞

lilactown 2020-12-16T01:28:35.458600Z

some other feedback: (current) calls a React hook, which cannot be called conditionally or in loops. This means that you cannot conditionally subscribe or create an event dispatcher, or in loops, with your current API

ghaskins 2020-12-16T02:57:33.460500Z

hey all, ive been using re-frame for a while now, but probably in a simplistic manner. I'm now trying to do something a little more advanced and struggling to figure out how to express what I think I need in a re-frame idiomatic way

ghaskins 2020-12-16T02:59:10.461700Z

in short, im trying to have an event from a graphql subscription trigger a few different components to do something, some of which is also async (e.g. reloading other things from the backend)


@ghaskins The graphql subscription should probably dispatch an event when new data arrives (dispatch [:new-x {:new-stuff XXX}]) and then the associated event handler will know where within app-db to place the new stuff and then, because data has been updated in app-db dominoes 4,5,6 will cause the new data to be rendered


So, the graphql subscription is treated like an "external agent" - like a user pressing a button - which triggers events via the arrival of new data. And then those new data events are handled in the normal way, and changes to the components rerender in the normal way.

ghaskins 2020-12-16T13:59:52.467900Z

@mikethompson ty for the response. That part I get. The thing I am struggling with (and perhaps I just need to approach the flow differently) is that currently the graphql subscription is just a generalized notification. Several other components then need to respond to the change by loading data contextualized to their state, such as filter/pagination settings.

ghaskins 2020-12-16T14:00:15.468400Z

so far, what ive got is working, but it seems a little hacky

ghaskins 2020-12-16T14:00:35.468700Z

basically im doing something like this

ghaskins 2020-12-16T14:02:27.470100Z

graphql-subscription -> rf/dispatch -> rf/sub(layer2) -> [[rf/sub(layer3) -> query ->rf/dispatch) [rf/sub(layer3) -> query ->rf/dispatch) [rf/sub(layer3) -> query ->rf/dispatch) ]

ghaskins 2020-12-16T14:02:48.470600Z

and the components finally sub to the data produced by the query dispatch

ghaskins 2020-12-16T14:03:27.471400Z

overall, it kind of makes sense, but the weird thing is I have to make sure I include a sub to the layer3 so that its included in the signal graph

ghaskins 2020-12-16T14:04:29.472Z

basically, the way its structured now is a type of pubsub, which I know goes against the grain of re-frame a bit


Hi, what is the recommended way of passing subscription values to life-cycle methods such as component-did-mount or component-did-update?

p-himik 2020-12-16T17:41:34.472800Z

I would try to avoid those methods altogether with reagent/with-let. If that's not an option and you really have to use those methods, then I would: - Create a sub outside of the reagent/create-class but within the component function. Do not deref it! - Deref it in the render function (it's fine if the value is unused - you just have to @ in there) - Deref and use in any life cycle method. The above only makes sense if the life cycle methods are called outside of the Reagent's ratom context. I have no idea if that's the case but you should be able to check it.


I am using these methods to update selection in content-editable which I want to fully control.


so the code looks like this:

    {:component-did-mount    #(do (r/after-render (partial js-selection/update-selection browser-selection))
                                  (js-input/add-before-input-event %))
     :component-did-update   #(r/after-render (partial js-selection/update-selection browser-selection))
     :component-will-unmount js-input/remove-before-input-event
     :display-name           "editor"
     :reagent-render         render})
where browser-selection is an output of a subscription.

p-himik 2020-12-16T17:48:25.473400Z

It does sound like reagent/with-let should work though. I may be wrong but I would definitely try it first.


how can I replace component-did-update with it?

p-himik 2020-12-16T17:52:26.473800Z

I think something like this:

(r/with-let [selection (subscribe [...])]
  (component-did-update ...)
  [:div ...]
    (component-will-unmount ...)))
Can off the top of my head think of the way to use component-did-mount. Maybe wrapping it in a delay.

p-himik 2020-12-16T17:58:06.474Z

Ah, even simpler - you can put it directly within the bindings block of with-let. Check out the example here:

p-himik 2020-12-16T17:58:18.474200Z

Where they call .addEventListener.


when are bindings in with-let executed? they create component-did-mount under the hood?


I have other usecases where I am using component-did-mount and component-did-update to calculate sizes of component elements when inserted into DOM

p-himik 2020-12-16T18:15:38.474800Z

There's some low-level magic specific to Reagent. I still don't quite understand its implementation. But AFAIK it's supposed to be able to replace life cycle methods in all or nearly all situations.


hmm, I don't think I get it enough to use it right now, and it seems to me like a source of complexity compared to life-cycle methods (which are also quite complex), so I went with you alternative suggestion for now


another option would be to create a wrapper component which would just subscribe and pass the value to the component itself as a parameter

p-himik 2020-12-16T18:20:15.475400Z

Ah, right - that is a common approach as well, especially when you work with JS libraries.


Thanks for help, I will probably have to read entire Reagent and Re-frame in the future to understand it better. And dig into React as well.

benny 2020-12-16T18:38:08.476700Z

trying to figure out ways to be more organized as our project gets bigger, any recommendations on event handler and subscription naming?

benny 2020-12-16T18:39:36.476800Z

like does anyone do something like this? :notifications/retrieve-all-notifications -> :notifications/ev-retrieve-all-notifications

p-himik 2020-12-16T18:44:18.477100Z

IIRC re-frame docs suggest you to give them a generic namespace, like user-events, and name them according to the actual action, like enter-text. I.e. :user-events/enter-text. I prefer to: - Not use any Hungarian notation-like prefixes (no -event, -events, -ev, etc) - Name according to the actual action - Use real namespaces and require and alias them when I want to use them. E.g.

  (:require [re-frame.core :as rf]))

(rf/reg-event-db ::do-stuff ...)

(ns <|>
  (:require [ :as cc]
            [re-frame.core :as rf]))

(defn app-view []
  [:button {:on-click #(rf/dispatch [::cc/do-stuff])} "Hello"])

benny 2020-12-16T18:50:45.477400Z

thanks @p-himik


@benny I’ve organized my app into modules which are just namespaces, e.g. my-app.modules.customer, my-app.modules.user, etc. Any Re-frame events/subs under my-app.modules.customer would have the prefix :customer/

benny 2020-12-16T18:51:54.477800Z

thanks @jkrasnay

benny 2020-12-16T18:52:07.478Z

i’ve taken a similar approach with modules, but i have some events and subs that look alike


Also, each module has a main.cljc where I register Re-frame things, as well as my own system for registering pages and endpoints.


The application main requires each of the module mains.

benny 2020-12-16T18:52:55.478600Z

funny, sounds like we landed on a similar approach, i called mine core


I use core for things that others can depend on, and main to depend on things. I find it helps avoid circular dependencies.

benny 2020-12-16T18:54:08.479Z

maybe i’m just being too picky, i just have a hard time seeing list-users and user-list


I find, though, that events and subs are often local to a given page, so I might call those something like ::user-list-init and ::user-list-init-success

p-himik 2020-12-16T18:55:25.479500Z

Interesting approach with main. So far I have only used core as well without any problems.

isak 2020-12-16T19:03:36.479800Z

Anyone do anything with suffixes? E.g., ! for events, and &gt; for subs (or &lt;)?


I don’t use special suffixes, but I do like to do this: (def &lt;- (comp deref rf/subscribe). Then inside my component I can subscribe like this (&lt;- [:some/sub])

clyfe 2020-12-16T19:14:54.480600Z my choice of organizing things there ^; rails influence

isak 2020-12-16T19:16:41.481Z

@claudius.nicolae cool idea. We do the same with macros, but didn't think of the rails part

clyfe 2020-12-16T19:16:45.481200Z

summary for standard apps: 1) exlusive use of namespaced keywords, 2) handler defined in the same namespace as keyword namespace 3) correlation between names


@ghaskins I'm not following/understanding your notation by it appears to be mixing subs with dispatches, which is a worry :-) Also, you use wording like "components then need to ..." and makes them sound quite imperative and causal. You might be interested in the lead-in to this FAQ entry: Tell me how you go.