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.
API sample: https://github.com/tape-framework/frame/blob/master/test/tape/devcards.cljs
the issue is that you're using binding
/ with-redefs
. you should just use context for everything
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
I misread, you are closing over the frame lexically
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 π
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
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
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.
@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.
so far, what ive got is working, but it seems a little hacky
basically im doing something like this
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) ]
and the components finally sub to the data produced by the query dispatch
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
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?
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:
(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.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?
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
.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
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
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
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.
trying to figure out ways to be more organized as our project gets bigger, any recommendations on event handler and subscription naming?
like does anyone do something like this? :notifications/retrieve-all-notifications
-> :notifications/ev-retrieve-all-notifications
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 <http://my-proj.app|my-proj.app>
(:require [my-proj.cool-component :as cc]
[re-frame.core :as rf]))
(defn app-view []
[:button {:on-click #(rf/dispatch [::cc/do-stuff])} "Hello"])
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/
thanks @jkrasnay
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 main
s.
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.
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
Interesting approach with main
. So far I have only used core
as well without any problems.
Anyone do anything with suffixes? E.g., !
for events, and >
for subs (or <
)?
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])
https://github.com/tape-framework/mvc#user-content-conventions my choice of organizing things there ^; rails influence
@claudius.nicolae cool idea. We do the same with macros, but didn't think of the rails part
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: http://day8.github.io/re-frame/FAQs/LoadOnMount/ Tell me how you go.