Fork me on GitHub
#re-frame
<
2016-08-31
>
fasiha02:08:06

Is it possible to get to re-frame/dispatch in plain JavaScript code? I’m doing something fiendishly despicable and want to dispatch re-frame events from plain JavaScript code. When I just evaluate re-frame/dispatch in the REPL, I see a short 5-line function, which makes me think this should be doable.

danielcompton02:08:17

re-frame would need to export dispatch to allow this

danielcompton02:08:28

open a PR, would be happy to review this

fasiha02:08:12

Well. Color me kooky and call me a monkey's uncle. That was easier than I thought. I’d like to make my "JS API" a bit simpler than what dispatch would accept, so I just have this in my Cljs:

(defn ^:export greet [event data]
  (if data
    (r/dispatch [(keyword event) data])
    (r/dispatch [(keyword event)])))

fasiha02:08:52

And I can make that as elaborate as it needs to be in Cljs, so I can, from JS, do evil things like, "<button onclick=\"my_ns.greet('say-hi', 123)\">click me</button>"

fasiha02:08:24

Big big thanks @danielcompton! I asked here expecting a re-frame-specific workaround. As usual, cljs has a generalized solution.

fasiha02:08:20

Especially happy that even my_ns namespace is available even in Closure-compiled JS, so the JS code is robust—unlike the rest of my app 😂

danielcompton02:08:04

yep, as long as it is ^:exported, otherwise it will be munged away with :advanced optimisations

michael.heuberger03:08:22

a question about the https://github.com/Day8/re-frame-forward-events-fx handler … it looks like you can post-process reg-event-fx handlers only, not our own effect handlers (`reg-fx`)

michael.heuberger03:08:36

is this intentional or should be supported as well?

mikethompson06:08:38

@michael.heuberger this is facility which allows you to post-process events themselves.

mikethompson06:08:31

So an event [:do :something] might have a associated handler (which might be of the -fx or -db variety, doesn't matter)

mikethompson06:08:08

And then you want that event "forwarded" to another event handler (could be -fx or -db, doesn't matter). It is almost as if, two events were dispatched: 1. [:do :something] 2. [:xxxx [:do :something]] (where you choose :xxxx )

mikethompson06:08:40

So .... I guess what I'm saying is that reg-fx doesn't really come into it (one of the handlers might, indeed, be of a -fx variety, and might return effects, but that's incidental to re-frame-forward-events-fx which is just about the on-sending of events)

pesterhazy09:08:46

@shaun-mahood thanks for the pointer!

reefersleep10:08:56

How do you ensure that an interceptor created with the after interceptor factory is run after each handler? Do you need to register the interceptor at each handler registration?

reefersleep11:08:44

Basically I want to check app-db against my schema upon calling each handler, without having to state so in every handler.

mccraigmccraig12:08:29

i say this rarely @reefersleep , but it sounds sounds like a job for a macro 🙂

lwhorton13:08:40

@reefersleep in my app I have a whole module called app_reframe to handle such cases… it’s a wrapper api around reframe that does things like make sure register-handler is actually register-handler [my-default-interceptors]

lwhorton13:08:37

i find it generally a good practice to do something like app_{library} for virtually all dependencies in an app so that you can maintain a consistent api throughout, even if the external api changes

reefersleep13:08:57

@lwhorton: that sounds like a good idea in general. I have also thought of just making a wrapper to ensure the correct default interceptors upon reg-event. Cheers for affirming my belief that this is the way to go 🙂

reefersleep13:08:02

@mccraigmccraig: I'm not sure how using a macro would help me out in this particular situation? A simple wrapper function should be sufficient, or did you have something more grand in mind?

mccraigmccraig13:08:59

@reefersleep my mistake - no need for macros - a wrapper fn is fine

reefersleep13:08:26

Alrighty then 🙂

martinklepsch13:08:59

@pesterhazy just had another thought regarding your data fetching Q from yesterday: Might be intresting to model data required for routes as coeffect. You navigate to a page, the coeffect checks if the required data is present, if not it adds an effect to get the data and discards the event or turns it into something like :loading :my-view. The callback for the data fetching should then re-emit the original navigation event this time the data will be present. User never sees a data-empty view. The :loading could even trigger some UI changes previewing the eventual UI structure (similar to what facebook does http://www.callumhart.com/blog/non-blocking-uis-with-interface-previews)

martinklepsch13:08:08

My use of coeffect above isn't quite right but I guess the idea gets accross 🙂

pesterhazy14:08:09

super interesting

shyambalu14:08:00

@lwhorton is your app open source

shyambalu14:08:12

i’m interested to check out how you did that

mikethompson14:08:23

@reefersleep yep, @lwhorton is correct. Create your own reg-event-db and use it to insert the interceptors you want

(def my-default-interceptors  [one two])

(defn my-reg-event-db  
    ([id handler]  
       (re-frame.core/reg-event-db id my-default-interceptors handler))
     ([id interceptors handler]  
        (re-frame.core/reg-event-db id [my-default-interceptors interceptors]  handler)))

From now on I use my-reg-event-db  to register events 
Remember that if reg-event-db is given nested vectors of interceptors it flattens before composing them So you can give it [[one two] nil three [four nil]] and that would become [one two three four]

reefersleep14:08:32

@mikethompson: That is almost verbatim my wrapper 😄

reefersleep14:08:40

I noted the flattening of vectors when reading the README, but decided to explicitly use (into my-standard-interceptors new-interceptors) in my example, in order not to confuse my teammates 🙂

reefersleep14:08:59

@mikethompson now that I have you - is there a smooth way to introduce re-frame into an application which already uses reagent? Preferably in a way so that the re-frame app-db becomes a smaller part of the already existing application r/atom.

mikethompson14:08:40

It depends, but probably not.

reefersleep14:08:46

We've implemented a neat state viewer (hopefully to be open sourced soon) which we use all the time when developing, but if I were to shunt state into re-frame's private app-db, we'd lose that functionality.

reefersleep14:08:26

Normally, we delegate parts of the state to cursors, and since the state viewer operates on the r/atom as a whole, this is not a problem. But putting state in a separate r/atom is.

mikethompson14:08:58

The "smoothness" you seek depends on the existing architecture adopted

mikethompson14:08:05

I'm skeptical because most non re-framian (I love that new term) reagent apps tend to have a slightly all over the place architecture. Different decisions at different points.

reefersleep14:08:22

Well, before reading more into re-frame, I had hoped that I could pass a cursor to re-frame, because that would mean that I could 'eat upwards' when introducing re-frame, gradually working from 'leaf' parts of the application towards the core.

mikethompson14:08:41

Well, its possible

reefersleep14:08:43

You're not wrong about that 😄

reefersleep14:08:06

How would you do that?

reefersleep14:08:21

I mean, I could fork re-frame. But ehm. I would prefer not to.

mikethompson14:08:27

app-db isn't private .... so it is available for any state viewer

mikethompson14:08:50

I mean you shouldn't be accessing it directly obviously. But you can get to it.

reefersleep14:08:28

I was thinking about something like a subscription that just returns the whole of @re-frame.core/app-db, and then swap that into a cursor, but it seems dirty/fragile

reefersleep14:08:50

Ah yes, @mikethompson, but the state viewer works upon a single r/atom 🙂

reefersleep14:08:22

It's a very particular problem, I know, it's just that the state viewer is such a central part of our development that it would hurt not to have it available.

mikethompson14:08:32

app-db is a single ratom

reefersleep14:08:57

yes, but we already have a single r/atom. With app-db, we would have two. 😄

mikethompson14:08:06

Oh, right. I see

pesterhazy14:08:29

@mikethompson, actually I've been thinking about that. Our app is using its pure reframe sofar, swapping and dereffing an atom freely. Would it be possible to add re-frame incrementally by search/replacing our-atom/ to reframe.db/app-db? We'd start using dispatch bit by bit then.

mikethompson14:08:54

So the problem is: 1. You have a very useful tool 2. Which you want to continue using 3. But it assumes one ratom 4. If you start to use re-frame, you'll have two for a period of time

pesterhazy14:08:07

it would be a huge refactoring (and harder sell) to change everything at once

reefersleep14:08:28

I'm trying to sell re-frame to the team here 🙂

reefersleep14:08:16

And both a gradual change with no state viewer or a big bang refactoring would hurt the sell.

reefersleep14:08:31

So it would be great if there was a smoother way forward.

mikethompson14:08:45

It doesn't sound like this is a solution, but I'll mention this: https://github.com/Day8/re-frame/blob/master/docs/FAQs/Inspecting-app-db.md

reefersleep14:08:05

But @pesterhazy brings up a good point, perhaps this would be a way forward (s/our-atom/re-frame/app-db)

reefersleep14:08:47

Good suggestions, @mikethompson, and as you said, not a solution 🙂 I wish I could just show you our state viewer, it is very slick. Keypress navigation, harmonicaing of lists/maps and more. You can quickly zoom in on your area of interest.

mikethompson14:08:13

I want it for re-frame 🙂

reefersleep14:08:34

I'm pushing for open sourcing it. I want it for my private stuff!

reefersleep14:08:45

But anyway. @pesterhazy perhaps has the best idea so far - replace every usage of our-atom with re-frame.db/app-db. Do you think that this is a viable path forward?

mikethompson14:08:20

Sounds like it. Both are ratoms.

mikethompson14:08:49

Perhaps your state inspector would allow you to switch backwards and forwards, or something

mikethompson14:08:31

(assuming you have a hybrid app)

reefersleep14:08:53

Well yeah, perhaps multiple r/atoms should be on the todo-list. I'm not sure. The guy developing it isn't present.

pesterhazy14:08:12

yay! a transition plan for re-frame

reefersleep14:08:44

@mikethompson the only thing I'm unsure of with using the s/our-atom/re-frame/app-db strategy is whether we would have some unfortunate side effect of accessing app-db directly in some (to begin with, close to all) code, and indirectly (and correctly!) via the event-handlers and subscriptions. What do you think?

mikethompson14:08:55

Can't think of any problem. You will be accessing it in read-only way ?

mikethompson14:08:09

Or does the inspector allow you to change state?

pesterhazy14:08:46

well an atom only supports swap!, deref and watch, it's not particularly magical

mikethompson14:08:17

I can't see a problem

pesterhazy14:08:20

so the way reframe uses a ratom (in subscriptions) shouldn't be too different from regular reagent-style derefs (or cursors or whatever)

pesterhazy14:08:45

@mikethompson, I'm quite excited about the "pure" side-effects for 0.8

mikethompson14:08:01

The way re-frame access app-db in subscriptions is the same way reagent accesses any ratom.

pesterhazy14:08:03

that's what's pushing me to reconsider re-frame (plus the docs)

mikethompson14:08:48

Effectful Event Handlers are neater for sure.

mikethompson14:08:27

because our dispatch is async, we could always get away with a bit previously. But it was nice to get it all cleaned up

mikethompson14:08:42

It has all fallen out pretty nicely

reefersleep14:08:20

@mikethompson Currently, the state viewer is read-only. It could change state at a point, and we may try to re-implement this, but that is less important and beside the point. The important part is that if there is no dice in both reading and writing to re-frame.db/app-db, then we can do a simple search and replace, so that everywhere that we have swap and @, we will be bypassing event handlers and subscriptions, we will be able to point our state viewer towards this new, one source of truth.

reefersleep14:08:22

So in other words - if you see no problem with directly swapping and dereffing re-frame.db/app-db in most code while using event handlers and subscriptions in some code, we can have a neat, gradual introduction of re-frame whilst retaining our state viewer 🙂

mikethompson14:08:33

I'm sitting here thinking that all through, and I can't think of any reason it won't work.

reefersleep14:08:12

Well, that sounds good!

mikethompson14:08:14

Still sitting here, trying to spot a flaw in the plan. Still nothing bad coming to mind.

mikethompson14:08:46

Nope. No immediate problem. Worth an experiment.

mikethompson14:08:08

Nite all. I'm off to bed

reefersleep15:08:06

@mikethompson Thank you for your input. And thank you for re-frame!

reefersleep15:08:26

@pesterhazy Thank you for the solution!

reefersleep15:08:51

Awesome. 😄 I'll make a note of making a big splash about it when we finally open source our state viewer.

shaun-mahood15:08:30

@reefersleep: Please do, it sounds great! Any idea of when / what chance it has of being open sourced?

reefersleep15:08:53

@shaun-mahood: There's drive towards open sourcing our stuff in general, somebody just have to figure out which licenses we're going to use. So chances are good of it happening soon-ish (that vague enough for you? 😉 )

shaun-mahood15:08:29

Perfectly vague :) Any thing else cool you want to open source? I love it when companies start down that path, so many useful tools that I love started that way.

nidu18:08:27

Hello, has anyone stumbled on an issue when subs with one and two sugar pairs work differently? E.g.

(reg-sub
  :plants/seed-price
  :<- [:app/config]
  :<- [:app/time]
  (fn [[config time]] ...))
works fine, but
(reg-sub
  :plants/seed-price
  :<- [:app/config]
  (fn [config] ...))
throws an error saying there's no subscription named :app/config? Is there anything i'm doing wrong? Guess i should mention that :app/config sub is :require'd earlier.

nidu18:08:27

In reg-sub method subscription is done as (subscribe (second input-args) for one sugar pair and as (map subscribe vecs). Maybe map acts lazy here and thus subscription happens only when required? What is the intended behaviour here?

nidu18:08:56

Explicitly requiring file with :app/config resolves an error

reefersleep19:08:53

@shaun-mahood: There might be a couple of things. I'm most impressed by the state viewer 🙂 I think we're all pretty open-sourcy, it's just policies and legalities getting in the way.

michael.heuberger22:08:19

@mikethompson thanks for explaining re: post-process events. lesson learnt. solved this by creating a new event handler next to the effect handler. that new event handler is just calling the effect handler and doing nothing else:

(reg-fx
  :router/start
  (fn [active-route]
    ...))

;; An event handler causing the above :router/start effect
;; Needed so that we can use them in forward events
(reg-event-fx
  :router/start
  [debug]
  (fn [_ [_ active-route]]
    {:router/start active-route}))

michael.heuberger22:08:33

probably a bit ugly but since reg-fx router/start is manipulating app-db further down, should be valid