Fork me on GitHub
#re-frame
<
2017-06-19
>
borkdude10:06:57

Does this pattern have a name in React/Re-frame land? Component A needs data from API endpoint /foo and dispatches an event to fetch it. However, it needs to dispatch an event after that to do its own thing with the data. Same for component B and endpoint /foo but also needs its own event afterwards. Also if the data requested for the same request is already present in the Re-frame db, use that (caching)

mattly16:06:19

@borkdude I don't think that pattern has an official name, but I've taken to calling it "dispatch forwarding", as in, "do your thing and then forward it here"

mattly16:06:04

If I weren't 100% sold on the re-frame way of doing things I'd probably just use CBP instead, with the CB handling the dispatch

frerom17:06:27

Hey all. Is there a nice way to measure the time it takes from a dispatch, a handler changing the db and for reagent to finish rendering components based on the db changes? (in production, without dev-tools)

mattly18:06:05

if I'm creating a form-3 component, do I put my subscriptions outside the reagent/create-class call or inside the :reagent-render function as I would for a form-2 component?

mattly18:06:20

I'm assuming the prior

mattly18:06:39

yeah, the example is a bit... too hypothetical

mattly18:06:00

perhaps a more pointed question: I need to somehow or another get the width of a component's container into the state for the render function, such that it will re-render on resize. Is there a best practice for doing this?

mattly18:06:31

I've got a plan forming on how to handle this, but if someone has written something up I'd love to see it

souenzzo18:06:18

About statefull components, you may need need: https://facebook.github.io/react/docs/refs-and-the-dom.html to "initialize".

valyagolev19:06:14

hi guys is my understanding correct, that reg-event-fx should not actually execute any side-effects? and reg-fx, which supposed to do that, can't return anything else to dispatch? i can't decide where to put my setTimeout producing effect, because I need to save the returned value

souenzzo20:06:18

1- There is a timeout handler by default on re-frame (but I cant find the name!!) 2- reg-event-fx functions should return a map that contains: - :db - A new, updated (or not) db. - ~other keys - Descriptions os side efects (for example, :do-timeout [100 :another-handler]

souenzzo20:06:45

(reg-fx :my-side-effect-function (fn [[time f]] (js/setTimeout time f))
(reg-event-fx :my-handler (fn [{:keys [db]} [_ param]] {:db (assoc db :waiting? true) :my-side-effect-function [1000 (stuff params)]}))
@valyagolev

valyagolev21:06:50

@souenzzo thanks! but what if i want to capture the return value of js/setTimeout ? as far as i understood from the docs, the reg-fx return is ignored?

mattly21:06:44

@valyagolev you'd have to, in the timeout function, dispatch the value to something else

mattly21:06:04

because really, nothing ever looks at the return value of the timeout function

valyagolev21:06:15

ah! so it’s like (reg-fx :hoarding-timeout (fn [[time f]] (dispatch :save-timeout-value (js/setTimeout f time)) ))

mattly21:06:30

there is no value in setTimeout(function(){ return 3 }, 1000)

valyagolev21:06:26

in js, setTimeout returns a special value which can be used to remove it

mattly21:06:43

(reg-fx :hoarding-timeout (fn [[time f]] (js/setTimeout #(dispatch :save-timeout-value (f)), time)))

mattly21:06:01

right, but the return value of the function supplied to setTimeout is ignored

valyagolev21:06:17

yeah, but i don’t care about it

valyagolev21:06:36

anyway my problem is solved, thanks, i didn’t realize one could manually dispatch events from reg-fx instead of returning them

mattly21:06:37

oh, you want the timer id

mattly21:06:46

sorry, I misunderstood

valyagolev21:06:19

np! it was probably not clear from my words, which of the many values involved is actually needed

valyagolev21:06:01

but well, if i just do it (setTimeout) in reg-event-db, is it considered low class or dangerous?

mattly21:06:14

I've solved this by putting the timeout generator in a cofx

mattly21:06:41

but that might not work for your particular case

souenzzo21:06:56

@valyagolev no problem. You can write all stuff in reagent... Without "a frame". But if you want to get "the power of re-frame", prefer pure functions

valyagolev21:06:27

that's very nice

valyagolev21:06:32

but what i'm asking for is advice on where to put my impure functions. unfortunately i have to use some. and the exact boundaries of pureness are not very clear. we have reg-fx, reg-event-fx and reg-event-data, and i'm wondering which of these require purity of functions, and which don't

mikethompson21:06:25

event handers should be pure effect handlers are not pure

mikethompson21:06:29

I'm assuming you store the js/setTimeout response so you can later remove it

mikethompson21:06:41

And that removal will be an effect too?

valyagolev22:06:06

(reg-fx
  :clear-alarms
  (fn [alarms]
    (.cancelLocalNotifications PushNotification (clj->js {:id 1}))
    (js/clearTimeout (:timeout alarms))))

(reg-fx
  :create-alarms
  (fn [date]
    (when-not (< date (js/Date.))
      (println date (clj->js date))
      (.localNotificationSchedule PushNotification (clj->js {:id 1
                                                             :message "a pomodoro!"
                                                             :date date}))
      (rf/dispatch [:save-alarms
                    {:timeout (js/setTimeout #(rf/dispatch [:alarm-ring]) (- (js/Date.) date))
                     :notification 1}]))))

valyagolev22:06:14

here's what i did right now

valyagolev22:06:22

but i was just wondering if that's the simplest way

valyagolev22:06:05

there's also

(reg-event-db
  :save-alarms
  (fn [db [_ alarms]]
    (assoc db :alarms alarms)))
and the code which dispatches to :create-alarms

valyagolev22:06:38

it's three reg-* handlers to handle one logic: 1. ask to create alarms 2. create alarms 3. save their ids

valyagolev22:06:42

now that i think about it, it's like javascript callbacks all over again...

mikethompson22:06:09

Checking: so there's only one (:timeout alarms) at a time?

valyagolev22:06:23

well, yeah, for now

mikethompson22:06:41

So two have two options ...

mikethompson22:06:44

1. You collapse both effect handlers into one

mikethompson22:06:02

You do clearing and creating in the one effect handler

mikethompson22:06:32

And you provide the "instruction" (`:clear` or :create) in the effect itself

valyagolev22:06:25

ah no, unfortunately there can be either no alarm at all or one alarm

valyagolev22:06:38

well, it still could work

valyagolev22:06:51

but the 3-step program to create an alarm is still there

mikethompson22:06:55

That's fine .... turning on an off the alarm is an effect

mikethompson22:06:22

So you need to create a small DSL for taking to an alarm manager

mikethompson22:06:38

Whenever you do an effect you are creating a tiny DSL

mikethompson22:06:32

So an effect (something returned by an event handler) could be like this ...

{:alarm-manager   {:action :create }}

mikethompson22:06:02

Or {:alarm-manager {:action :clear}}

mikethompson22:06:42

Notice that there's now only one effect handler :alarm-manager. It can maintain whatever state you want

valyagolev22:06:10

how would it maintain state? using its own atoms inside?

mikethompson22:06:35

That would work, perhaps like this ...

(def-fx  
   :alarm-manager 
   (let [alarm-state  (atom)]
      (fn [instruction] 
         (if (= :create (:action instruction))
              .....
              .....)))

valyagolev22:06:20

ah i see, that's nice

valyagolev22:06:05

a question though, why not let def-fx receive and return :db as well?

valyagolev22:06:27

that would collapse the whole thing into something small

mikethompson22:06:50

Its is tricker than you might initially realise ..

mikethompson22:06:55

Ordering is a big deal

mikethompson22:06:10

Which effects get done in what order

mikethompson22:06:24

At the moment, updating app-db is not different to any other effect

mikethompson22:06:47

But suddenly it has to be done last, after all the other effects, so it becomes special

mikethompson22:06:16

And there are some cases where you might want app-db writen first

mikethompson22:06:39

I can't remember the usecases now, but I remember being able to imagine cases like that

valyagolev22:06:57

yeah, i see but maybe another way of looking at it would be helpful. the :db handler seem to just do reset!, right? maybe more swap!-like logic would really help with cases like that

mikethompson22:06:00

Yeah, all possible. But I came down on the side of simplicity and giving no guarentees.

mikethompson22:06:19

That left open the possibility of later on, with more experience, adding guarentees (about ordering etc)

mikethompson22:06:45

But ... it has never been enough of a problem to move away from what I initially did

valyagolev22:06:08

another thing i was wondering about re-frame was whether there was a way to dispatch events based on, hmm, some logical relationships between values? probably an example would be better. say i have a db: {:a 3 :b 4}, and a variety of events which just do things like inc and dec and i have to do something whenever (:a db) = (:b db) so - very *pseudo*code:

(let [a @(rf/subscribe [:a])
      b @(rf/subscribe [:b])]
    (if (= a b) (rf/dispatch [:equality!])))

valyagolev22:06:37

it seems that re-frame could support those things very easily, saving me effort to compare the values in every event

mikethompson22:06:28

Do many parts of your system change paths [:a] and [:b] ?

valyagolev22:06:58

yeah, plenty

mikethompson22:06:03

Well, i guess the question is; do many event handlers change those paths

valyagolev22:06:20

yeah, and they change it in different ways

mikethompson22:06:35

Is it easy to add an interceptor to those event handlers ?

valyagolev22:06:40

so i could put like a (dispatch [:compare-and-maybe-fire]) everywhere

mikethompson22:06:25

It kinda sounds like you want a variation on on-change

valyagolev22:06:30

but i think this is a very "declarative" situation, right? so maybe there could be a way to subscribe to all changes to :a or :b filter them by equality, etc

valyagolev22:06:26

hmm i'll check it out

mikethompson22:06:56

That's one way of doing it ... add an interceptor to all event handlers which could potentially change those two paths.

mikethompson22:06:03

That does mean you have to remember to do it (add the interceptor)

valyagolev22:06:23

it's kind of weird that only the UI code can subscribe to stuff

mikethompson22:06:03

Which does not deal directly with your need

mikethompson22:06:18

Back to your need ... you can certainly hack it together ...

mikethompson22:06:06

(reagent.core/track!
    fn []
      (let [a @(rf/subscribe [:a])
             b @(rf/subscribe [:b])]
          (if (= a b) (rf/dispatch [:equality!])))
     ))

mikethompson22:06:32

I can't remember the params to track! off the top of my head ... but something like that

mikethompson22:06:50

That will permanently be looking at [:a] and [:b]

mikethompson22:06:26

If you need to turn the tracking on and off ... you'd have to create an effect to do it :-)

mikethompson22:06:54

Gotta go. Good luck

valyagolev22:06:01

haha no, for on-off thing i'd rather do

(reagent.core/track!
    fn []
      (let [is-tracking-on? @(rf/subscribe [:is-tracking-on])]
             a @(rf/subscribe [:a])
             b @(rf/subscribe [:b])]
          (if (= a b) (rf/dispatch [:equality!])))
     ))

mikethompson22:06:10

Well, I'd probably prefer to turn on and off tracking via an effect handler. But yes, what you suggest will work

valyagolev22:06:01

thanks, good luck. i have some thoughts on the issue you sent, i'll try and send them to you in a bit, we could go on discussing it if you ever have time and interested

mikethompson22:06:26

I'm interested, but very short on time

mikethompson22:06:46

Right now. If you have thoughts perhaps open issues or add to them

mikethompson22:06:55

Then I can cycle back

valyagolev22:06:24

yeah thanks! i'll see what i can do to make my ideas as useful as possible

mikethompson22:06:51

I love all input, although sometimes it is hard to find the time to respond effectively

valyagolev22:06:38

yeah now i think i'll have to read more of the docs to see for which reasons you didn't embrace more reactive approach (as in Elm, etc)

souenzzo23:06:24

@valyagolev :dispatch-later is the default fx...