Fork me on GitHub
#re-frame
<
2018-11-24
>
andrea.crotti12:11:46

Ah nice thanks easier than I thought 😄

isker16:11:45

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)

isker16:11:52

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 😉

lilactown17:11:04

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

isker17:11:13

mostly whether anyone has heard of this happening before 🙂

lilactown17:11:17

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

lilactown17:11:18

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

isker17:11:10

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.

drewverlee17: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?

lilactown17:11:01

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

manutter5117:11:47

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.

manutter5117:11:11

Once per component in the DOM, that is.

drewverlee17: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?

drewverlee17:11:33

thanks for the feedback btw

lilactown17:11:39

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

manutter5117:11:47

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

lilactown17:11:27

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

lilactown17:11:36

even if you pass in new props x

☝️ 1
manutter5117:11:42

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

lilactown17:11:44

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

drewverlee17: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?

lilactown17:11:37

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

drewverlee17:11:02

ah, i see what your saying

lilactown17:11:38

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

lilactown17:11:52

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

lilactown17:11:02

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

lilactown17:11:41

(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]...

lilactown17:11:44

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

lilactown17:11:16

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

lilactown17:11:43

when foo changes then bar’s subscription will update accordingly

lilactown17:11:42

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

drewverlee17:11:28

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

lilactown18:11:38

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

manutter5119:11:02

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.

👍 1
heyarne19:11:59

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?

lilactown20:11:11

is the effect synchronous?

lilactown20:11:29

e.g.:

(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

lilactown20:11:49

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

lilactown20:11:26

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

heyarne20:11:34

the effect itself is synchronous, yup

lilactown20:11:00

then the whole thing should be synchronous AFAIK

lilactown20:11:13

at least, up until the re-render

heyarne20:11:56

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

heyarne20:11:32

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

heyarne20:11:35

and doesn't allow it

heyarne20:11:53

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

heyarne20:11:01

well then i have a bug somewhere else 😉

lilactown20:11:19

hmm interesting

heyarne20:11:47

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

heyarne20:11:09

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

drewverlee20: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?

lilactown20:11:36

they have slightly different characteristics

lilactown20:11:19

you’re talking about the difference between:

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

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

lilactown20:11:09

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

lilactown20:11:48

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

lilactown20:11:00

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

drewverlee20: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?

lilactown20:11:10

replace foo with to-do

lilactown20:11:31

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

drewverlee20:11:48

you would subscribe to a todo-list then right?

lilactown20:11:28

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

lilactown20:11:04

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

drewverlee20:11:39

i see what you mean. thanks!

👍 1