Fork me on GitHub
#re-frame
<
2020-12-16
>
clyfe01:12:46

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.

lilactown01:12:07

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

lilactown01:12:25

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

lilactown01:12:14

I misread, you are closing over the frame lexically

lilactown01:12:05

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 😞

👍 3
lilactown01:12:35

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

😯 3
ghaskins02:12:33

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

ghaskins02:12:10

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)

mikethompson05:12:01

@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

mikethompson05:12:33

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.

ghaskins13:12:52

@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.

ghaskins14:12:15

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

ghaskins14:12:35

basically im doing something like this

ghaskins14:12:27

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) ]

ghaskins14:12:48

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

ghaskins14:12:27

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

ghaskins14:12:29

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

Pavel Klavík17:12:55

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

p-himik17:12:34

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.

Pavel Klavík17:12:09

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

Pavel Klavík17:12:50

so the code looks like this:

(r/create-class
    {: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-himik17:12:25

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

Pavel Klavík17:12:14

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

p-himik17:12:26

I think something like this:

(r/with-let [selection (subscribe [...])]
  (component-did-update ...)
  [:div ...]
  (finally
    (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-himik17:12:06

Ah, even simpler - you can put it directly within the bindings block of with-let. Check out the example here: https://reagent-project.github.io/news/news060-alpha.html

p-himik17:12:18

Where they call .addEventListener.

Pavel Klavík18:12:38

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

Pavel Klavík18:12:25

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-himik18:12:38

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.

Pavel Klavík18:12:10

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

Pavel Klavík18:12:40

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-himik18:12:15

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

Pavel Klavík18:12:31

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.

👍 3
benny18:12:08

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

benny18:12:36

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

p-himik18:12:18

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.

(ns my-proj.cool-component
  (:require [re-frame.core :as rf]))

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


(ns 
  (:require [my-proj.cool-component :as cc]
            [re-frame.core :as rf]))

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

🎯 3
jkrasnay18:12:45

@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/

benny18:12:07

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

jkrasnay18:12:13

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

jkrasnay18:12:31

The application main requires each of the module mains.

benny18:12:55

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

jkrasnay18:12:03

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

👍 3
jkrasnay18:12:14

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

✔️ 3
p-himik18:12:25

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

isak19:12:36

Anyone do anything with suffixes? E.g., ! for events, and > for subs (or <)?

jkrasnay19:12:44

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

rage4 3
👀 3
✔️ 3
clyfe19:12:54

https://github.com/tape-framework/mvc#user-content-conventions my choice of organizing things there ^; rails influence

isak19:12:41

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

🙂 3
clyfe19:12:45

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

✔️ 3
mikethompson19:12:32

@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: http://day8.github.io/re-frame/FAQs/LoadOnMount/ Tell me how you go.