Fork me on GitHub

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.


re-frame would need to export dispatch to allow this


open a PR, would be happy to review this


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


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>"


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


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 😂


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


a question about the handler … it looks like you can post-process reg-event-fx handlers only, not our own effect handlers (`reg-fx`)


is this intentional or should be supported as well?


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


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


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 )


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)


@shaun-mahood thanks for the pointer!


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?


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


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


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


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


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


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


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


Alrighty then 🙂


@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


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


super interesting


@lwhorton is your app open source


i’m interested to check out how you did that


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


@mikethompson: That is almost verbatim my wrapper 😄


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 🙂


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


It depends, but probably not.


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.


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.


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


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.


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.


Well, its possible


You're not wrong about that 😄


How would you do that?


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


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


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


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


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


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.


app-db is a single ratom


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


Oh, right. I see


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


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


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


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


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


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


It doesn't sound like this is a solution, but I'll mention this:


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


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.


I want it for re-frame 🙂


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


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?


Sounds like it. Both are ratoms.


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


(assuming you have a hybrid app)


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


yay! a transition plan for re-frame


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


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


Or does the inspector allow you to change state?


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


I can't see a problem


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


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


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


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


Effectful Event Handlers are neater for sure.


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


It has all fallen out pretty nicely


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


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 🙂


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


Well, that sounds good!


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


Nope. No immediate problem. Worth an experiment.


Nite all. I'm off to bed


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


@pesterhazy Thank you for the solution!


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


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


@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? 😉 )


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.


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

  :<- [:app/config]
  :<- [:app/time]
  (fn [[config time]] ...))
works fine, but
  :<- [: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.


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?


Explicitly requiring file with :app/config resolves an error


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


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

  (fn [active-route]

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


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