Fork me on GitHub
#re-frame
<
2019-11-04
>
knubie18:11:15

Anyone have an idea of best practice for dispatching an event (effect) every time a subscription changes?

manutter5118:11:34

What’s the context?

knubie18:11:05

@manutter51 I'm building an electron app and I want to update the "badge count" on the dock icon in response to a subscription (`:cards/due-today-count`)

mhuebert10:11:45

I might write a component that sets the badge count declaratively. Eg somewhere in your app, you have [badge-count @(subscribe ...)]. The badge-count component can use :component-did-mount & :component-did-update to fire the necessary side effects - these are implementation details hidden behind a declarative API, like how React hides DOM manipulation. You can then treat it as ‘just another view,’ even though you are displaying something outside the react tree. I use an approach like this for setting things like window.document.title

knubie18:11:23

Updating the app's icon badge count requires sending an API call to the electron process

manutter5118:11:34

Ok, so probably there’s some event that’s updating the app-db value that’s feeding the subscription, right?

knubie18:11:19

In this case there are a few events that could affect the subscription

manutter5118:11:36

:thinking_face:

knubie18:11:55

I thought about creating an effect and attaching it to all of those handlers

knubie18:11:04

but it doesn't seem very DRY

knubie18:11:09

compared to listening to that single subscription

lilactown18:11:11

you probably shouldn't dispatch a re-frame event from a subscription, but you can do side-effects based on a subscription by using add-watch or a reagent reaction

knubie18:11:54

@lilactown is add-watch part of the reagent api?

lilactown18:11:06

maybe it would be fine to dispatch a re-frame event, but if you update the db it might go into an infinite loop if you're not careful

✔️ 4
lilactown18:11:34

reactions and ratoms implement the protocols necessary to use add-watch, which is in the core library, I believe

isak18:11:52

Another thing that can work if you're willing to add it multiple places (and maybe restructure a little bit): https://github.com/day8/re-frame/blob/master/src/re_frame/std_interceptors.cljc#L319-L324

knubie18:11:46

run! seems appropriate for this use case. Would I just call that in e.g. the top level component? Feels odd putting something like that in a component

knubie18:11:53

I guess it doens't really need to go in a component does it

isak18:11:55

it depends how parameterized your component is. It if it is effectively "global", then at the top level is fine. Sometimes I've done it via "track!" (IIRC), which is similar, but can be disposed in component-will-unmount, so it is appropriate if the subscription requires parameters.

lilactown18:11:48

I wouldn’t put it in a component. I would put it in my function that starts the app, or maybe even a defonce to prevent it from getting executed multiple times

lilactown19:11:35

I wouldn't do it in with-let because you still might re-mount the component when you restart the app with some changes

isak19:11:25

well if your subscription requires parameters, that is the correct thing though

lilactown19:11:36

that's why I would move it outside of a component entirely, probably something like:

(defonce update-badge-count (run! ...))

isak19:11:01

I mean if the vector passed to the subscription is dynamic

lilactown19:11:10

you can pass any parameters you want when you (run! ...)

knubie19:11:22

In this case case the params are not dynamic

knubie19:11:26

it's sort of a global subscription

isak19:11:27

right, but how to manage the lifecycle?

isak19:11:34

ok, nm then

lilactown19:11:00

you can do it manually (using add-watch / remove-watch or a reaction directly) or you just don't care

lilactown19:11:14

this use case is distinct from updating the DOM from a subscription

lilactown19:11:31

this is "fire off arbitrary side effect based on a subscription" which shouldn't involve reagent/react/DOM at all

knubie19:11:12

Yeah, I think not having it in a component makes sense, because it's not a view concern really

isak19:11:04

in my case, usually there is some kind of scope (and therefore component) for which it makes sense to have a thing running, and I always regret not doing something like that, but maybe it is different for you

knubie19:11:54

That's a good point, but I think in my case it makes sense for this thing to always run as long as the app i open

4
mhuebert10:11:45

I might write a component that sets the badge count declaratively. Eg somewhere in your app, you have [badge-count @(subscribe ...)]. The badge-count component can use :component-did-mount & :component-did-update to fire the necessary side effects - these are implementation details hidden behind a declarative API, like how React hides DOM manipulation. You can then treat it as ‘just another view,’ even though you are displaying something outside the react tree. I use an approach like this for setting things like window.document.title