Fork me on GitHub
#re-frame
<
2018-09-29
>
drewverlee03:09:51

What do you mean credentials federation? @ghopper

Garrett Hopper06:09:25

@drewverlee, I take Google openid credentials after a user logs in and do a federated sign in to AWS to get a temporary access token. That's not really the relevant part here though.

Garrett Hopper15:09:16

I think I've come up with a solution, though it still feels like I'm doing something wrong. Some feedback on the name of this function would be great. 🙂

(defn make-value-reaction [f]
  (let [a        (r/atom nil)
        reaction (ratom/make-reaction #(f a))]
    @reaction
    (ratom/make-reaction
     (fn [] @a)
     :on-dispose #(ratom/dispose! reaction))))

(rf/reg-sub-raw
 :example
 (fn [_]
   (make-value-reaction
    (fn [a]
      (let [sometimes-nil @(rf/subscribe [:sometimes-nil])]
        (if sometimes-nil
          (reset! a (make-thing-when-not-nil sometimes-nil))
          (reset! a nil)))))))

Garrett Hopper15:09:30

What's happening here is that I want to have a subscription that always returns the same instance no matter how many times it's referenced and gets updated only when [:sometimes-nil] is changed. I think the only way to do this is with the two reaction thing. Otherwise I end up with a cascade where a reaction causes itself to update continually.

Garrett Hopper15:09:16

Also, this lets me handle async functions which return nil at first until they have data:

(rf/reg-sub-raw
 :value
 (fn [_]
   (make-value-reaction
    (fn [a]
      (let [example @(rf/subscribe [:example])]
        (when example
          (.then (.getData example)
                 #(reset! a (js->clj %)))))))))

mikethompson21:09:35

@ghopper this may all fall out rather easily if you treat a promise the same way you treat an HTTP request

mikethompson21:09:37

That approach assumes you will store the results of the HTTP (or promise) in app-db

drewverlee23:09:52

really? i thought the library handed back a parsed body and you could do with that what you wanted, why does it need to be put into the db.

mikethompson00:09:22

@drewverlee Seems a reasonably likely outcome to assume. An event is dispatched with the HTTP result. The associated event handler (which processes that event containing the result) will likely put some aspect of that result into app-db. Other paths are possible, but unlikely.

mikethompson00:09:46

So my point is: the outcome of a promise can be processed in the same way as the outcome of an HTTP GET.

drewverlee00:09:36

Oh yeah, I see. Just making sure I didn't miss something

mikethompson21:09:54

And the subscriptions simply deliver that through to the views

mikethompson21:09:14

The struggle you have at the moment is that your subscriptions are doing too much

Garrett Hopper21:09:57

Oh, that makes sense. I wasn't thinking the result of this could be put in the db via a second dispatch.

mikethompson21:09:16

yeah, the then of the promise just does a dispatch

mikethompson21:09:41

The same as the on-success of the HTTP request

Garrett Hopper21:09:04

Where the act of subscribing set up and tore down the request. (Eventually there will be a websocket connection as well.)

mikethompson21:09:24

Yeah, if I had time, I'd redo that documentation or possibly remove it. It was more of an experiment to document that process (which has a few moving parts). There are some caveats at the bottom.

mikethompson21:09:51

websockets just dispatchwhen something arrives

mikethompson21:09:20

The arrival is an event in the same way as a user clicking on something

Garrett Hopper21:09:57

What would you use to start and end that websocket connection?

mikethompson21:09:13

An effect handler

mikethompson21:09:40

Anthing that mutates the world (starts and ends websockets) is an effect

Garrett Hopper21:09:22

That would need to be called by an event, right?

Garrett Hopper21:09:46

Ah, it looks like this is what I'm looking for. :thumbsup:

mikethompson22:09:02

Yes, your re-frame system is event driven. Nothing happens unless there is an event. So websocket only gets opened because of some event or other. So it will be the event handler which knows to now open the websocket. And it will produce an effect

Garrett Hopper22:09:54

So, should I have that event triggered inside a React lifecycle method, if I want the websocket connected while the component is visible?

Garrett Hopper22:09:08

Heh, clearly I've missed a lot of these. 😛

mikethompson22:09:43

Also be sure to read the link in that FAQ to http://PurelyFunctional.tv

Garrett Hopper22:09:51

@mikethompson, so I have a client, which needs to always be re-created whenever credentials changes. This client should be in db, so I can easily pull it out for rf/reg-fx, right? How should I go about keeping it updated whenever (rf/subscribe [:credentials]) changes? (`:credentials` is also stored in the db.)

Garrett Hopper22:09:01

Up until now, I've been using the fact that re-frame deduplicates subscriptions to my advantage, though I have to then pass the client through the (rf/dispatch) which passes it on to the effects that need it.

Garrett Hopper22:09:37

(Meaning, I just have a subscription that uses [:credentials] as an input and returns a new client.

Garrett Hopper22:09:35

Oh... duh. There's an event that changes :credentials which should just issue another event to re-create the client.

Garrett Hopper22:09:44

I think it's all starting to click for me. 😉

mikethompson22:09:54

Yeah, remember that re-frame is event driven. It is events which move the system from one state to another. Every event cause some "change": a change to app-db or opening a web socket. These are the effects.

mikethompson22:09:31

subscriptions simply deliver data

mikethompson22:09:44

They are not imperative

mikethompson22:09:52

They do not cause things to happen

mikethompson22:09:00

(only events cause things to happen, and even then ... only via effects)

Garrett Hopper22:09:29

It's starting to make sense. 🙂 Thanks for your patience.

Garrett Hopper22:09:06

So if I were to have a subscription that pulls some derived data out of the database (the sum of multiple items or something), and I were to need that value inside an effect handler, I would have to create a new function, that accepts the db, for the subscription handler and the effect handler to use, right?

mikethompson22:09:12

@ghopper yeah, I think that sounds right

mikethompson22:09:28

The value could be also be computed and cached in app-db

mikethompson22:09:49

If multiple parts of the system need it.

Garrett Hopper22:09:29

Ah, good point. I hadn't thought about caching it.