Fork me on GitHub

Ah nice thanks easier than I thought 😄


anyone heard of re-frame being used in a chrome extension before? the relevant challenge is that the app-db would have to live in what's called a "background script" that is communicated with asynchronously by short-lived clients that render the actual HTML (in my case "popup pages", which are created when you click on the extension's icon next to the browser bar and are destroyed when you click away)


i feel like this should definitely be possible because event handling is asynchronous in the first place (just adding some additional asynchronous "IPC" via these chrome APIs into the mix) but the question for me is whether it'd be worth it 😉


I’m not sure what you’re asking. Is it possible? Absolutely. Is it good? Maybe?


mostly whether anyone has heard of this happening before 🙂


I imagine it would be, since coeffects + dispatch should be an easy way to model the message passing to and from your app


I see some people talking about it on reddit but no code


i think i will have to apply my forehead to the wall on this for a bit to come up with some better questions 🙂. thanks for looking.

Drew Verlee17:11:09

This is a re-frame + reagent question. If you setup a component like so:

(defn c [x] 
    (let [foo (subscribe [:foo])
       (fn [x]
            (let [bar (subscribe [:bar @foo]
                   zoo (subscribe [:zoo]...
possible you should never set things up this way, and thats part of my confusion. But assume this is a valid setup, then its a bit unclear to me how and when things are evaluated. 1. when c is called i would assume everything updates and we return a new render function. 2. when :foo changes but c hasn't been called with a new x then i assume the render function returned from c doesn't change as it was never called. This is probably the confusing part to me, as a subscription is global, so the alternative seems plausible as well. 3. when c is called then i assume we return a new render function. If then bar changes then we don't change the render functions output?


AFAIK, in reagent you will need to create the dependent subscription (subscribe [:bar @foo]) in another component


I would expect that c is only called once, to obtain a rendering function, and that the rendering function is the only thing called thereafter.


Once per component in the DOM, that is.

Drew Verlee17:11:52

I mean, if there is another view that c is part of, then if the arguments to c, change, i would expect it to update right? Ill put it this way, does a subscription inside a view, always mean that view is re-rendered if the subscription updates? like if :zoo updates, it would take the value we closed over for @foo the last time c's render function ran and pass that in?

Drew Verlee17:11:33

thanks for the feedback btw


i. it depends on if you dereference the subscription ii. with a form-2 component, the intial function body (let [foo ...] is only run once per instantiation


I would not expect c to be called a second time if the value of x changes, unless x is a subscription.


so (subscribe [:foo]) in this case will only ever be run once, unless you completely blow the UI tree away and re-instantiate the component


even if you pass in new props x

☝️ 4

It would be easy enough to throw in some println's or console.log's and see if I'm right tho 🙂


your second point @drewverlee is correct, that when the view is re-rendered (the returned function is re-run) it keeps all closed over values e.g. foo

Drew Verlee17:11:53

> I would not expect c to be called a second time if the value of x changes, unless x is a subscription. that makes sense to me. > so (subscribe [:foo]) in this case will only ever be run once, unless you completely blow the UI tree away and re-instantiate the component > even if you pass in new props x thats if x isn't a subscription itself right?


so it depends on if you dereference subscription x in the initializing function

Drew Verlee17:11:02

ah, i see what your saying


if you dereference it, then it will re-run wherever you dereference it when that ratom emits a new value


passing in a new subscription/ratom will not cause it to be re-run


and if you don’t dereference it, it will not be re-run


(defn c [x] 
    (let [foo (subscribe [:foo])] ;; <-- ran only once per instantiation
       (fn [x]
            ;; these will be ran on each re-render
            ;; e.g. a new `x` passed in or `foo` updates
            (let [bar (subscribe [:bar @foo])
                   zoo (subscribe [:zoo]...


now let’s say you dereference both bar and zoo inside of the body of your render fn


your component will probably behave as you would kind of expect; it will re-render any time foo bar or zoo change


when foo changes then bar’s subscription will update accordingly


the only kinda-bummer is that you are subscribing and disposing the old subscription on each render. but actually subscribe might be memoized, I can’t remember

Drew Verlee17:11:28

hmm, ok. i might get it. I'll need a minute to try things out and let it sink in.


there are also some tricks to get c to completely re-initialize when x changes if you want to know 😉


The latest version of re-frame does do subscription caching, so it's cool to subscribe and de-ref in the same line of code.

👍 4

if i cause an effect from inside an event-handler, is that effect handled synchronously? in other words, if i dispatch an event via dispatch-sync in an :on-click handler, is the complete process synchronous?


is the effect synchronous?



(reg-event-fx :foo
  (fn [cofx _]
    (async-fetch-data) ;; <-- asynchronous AJAX request, will not wait
    (assoc cofx :db {:foo "fetching"}})))
the function will not wait for async-fetch-data to complete before returning


dispatch-sync is purely to tell re-frame, “handle this event RIGHT NOW”


otherwise, re-frame will add the dispatched event to a queue and handle them in order


the effect itself is synchronous, yup


then the whole thing should be synchronous AFAIK


at least, up until the re-render


my problem is safari being weird about playing audio. i do click button -> create element, set src, play it basically


and if play isn't called in the event handler of the click (i.e. when the user interacts) safari thinks somebody is trying to annoy the user by autoplaying


and doesn't allow it


but if i can just call dispatch-sync and everything's fine


well then i have a bug somewhere else 😉


hmm interesting


i created an example which seems to indicate you're right


it didn't work in a larger application though, so i just wanted to ask to make sure

Drew Verlee20:11:43

so it would seem there are two ways to approach getting data to your views, with reagent and re-frame. The view can have subscriptions that query the data and your view just subscribes to that, or the data can be passed into the view and from their be used in subscriptions or used directly. The methods would seem to be interchangeable, is that right?


they have slightly different characteristics


you’re talking about the difference between:

(defn show-foo []
  (let [foo (subscribe [:foo])]
    (fn [] [:div @foo])))
(defn show-foo [foo]
  [:div foo])

;; elsewhere
(defn app []
  (let [foo (subscribe [:foo])]
    (fn [] [show-foo @foo])))


they’re not exactly the same, right? the first one has an explicit dependency on the :foo subscription to get it’s data


say we had a ton of foo’s to render in our app, we wouldn’t want to subscribe to a single foo inside of the same component that defines how it should look and behave


there’s also the matter of displaying the component’s various UI states in devcards and testing it

Drew Verlee20:11:53

> say we had a ton of foo’s to render in our app, we wouldn’t want to subscribe to a single foo inside of the same component that defines how it should look and behave Sorry, what does this mean? why would you need to subscribe to foo more then once in a component?


replace foo with to-do


you might have 1, 10, or 5000 to-dos on your page at a time

Drew Verlee20:11:48

you would subscribe to a todo-list then right?


right, you would subscribe to the list in a component that would then pass the data for each to-do in as props


at work I encourage people to distinguish between “container components” which conjure up all of the state (like subscribing to re-frame), and “presentational components” which handle actually how to render and behave based on the state passed in as props

Drew Verlee20:11:39

i see what you mean. thanks!

👍 4