Fork me on GitHub
#re-frame
<
2018-07-03
>
gspringtech04:07:33

I’m using re-com/single-dropdown with the filter box - is it possible to automatically select the last remaining item in the filtered list? The user could then press Return to select.

gregg05:07:55

Not automatically. Manually, you might be able to setup a keyboard event listener and...hmmm, nah I don't think that would work either because you would have to be privy to the changing state of filtered-choices which is internal

gspringtech05:07:40

I’ll have to dive into the re-com code then. Thanks.

pesterhazy10:07:30

A bit of a basic question, maybe someone can help me understand. Suppose I have two comps:

(defn inner [x]
  (let [y @(rf/subscribe [:y])]
    [:div (pr-str (= x y)]))

(defn outer []
  (let [x @(rf/subscribe [:x])]
    [inner x]))
Assuming that :x and :y are keys of app-db and assuming that they both are always swap!ped together to contain the same number, is it ever possible for x and y to diverge in inner?

pesterhazy10:07:50

In particular I'm interested in the possibility where the subscription in inner fires before outer, creating a temporary state of (!= x y)

pesterhazy10:07:29

I'm guessing this is impossible, but I'm curious what guarantee there is that you never see this in-between state.

guy10:07:48

I thought event wise, it would update say x first then y

guy10:07:25

I don’t understand how you would swap them together i guess

pesterhazy10:07:41

(reg-event-db :inc
              (fn [db _]
                (-> db
                    (update :x inc)
                    (update :y inc))))

pesterhazy10:07:18

as a simple example ^^

guy10:07:33

righhhht sorry

guy10:07:38

So that returns the entire app state but with those two values updated

guy10:07:01

I understand your question now haha sorry

guy10:07:02

I don’t know the answer to your question but i would guess,

3-4-5-6 Summary
The key point to understand about our 3-4-5-6 example is:

a change to app state ...
triggers query functions to rerun ...
which triggers view functions to rerun
which causes modified browser DOM
Perhaps whichever function is rendered first?

guy10:07:47

I’m interested to know the answer too now 😅

pesterhazy10:07:25

I think there's some guarantee that outer will rerender before inner, but I wonder if it's documented/made explicit somewhere

guy10:07:53

I’ll have a quick google :thinking_face:

pesterhazy10:07:36

if outer rerenders first, you'll never see a mix of old props, passed in from outer, and new subscription results

guy10:07:54

So just to be clear, this isnt to do with order of events

guy10:07:59

but order of re-rendering right?

pesterhazy10:07:38

correct. With my example, app-db will be updated atomically so there won't be a situation where (!= x y)

guy10:07:10

Ok ill have a google and if i find anything ill ping ya :thumbsup:

p-himik17:07:10

Hi. I'm building an application that must be able to switch between workspaces (ws) - basically, switch among a few paths inside app-db as default data sources for ~80% of all subscriptions and event handlers. An example of app-db.

{:workspaces {:w1 {...}
              :w2 {...}}
 :current-ws :w1
 :some "global data"
 :some-other "global data"}
Because of how effects work, some :dispatch effects may be executed with a ws different from the current one. It makes it not so trivial to implement. One way to implement such switching would be to pass current ws to each and every subscription and event handler that requires it. The solution is simple but not easy; it will be possible to monitor what ws is used by each subscription/handler but apart from passing ws everywhere, I will have to remember which subscriptions/handlers require ws and which do not. Another way would be to attach ^{:ws ws} metadata to all events by writing my own reg-event-(db|fx) that checks for ws metadata and uses (:current-ws app-db) if none available. The solution is not that easy and not that simple - I will have to rewrite some pieces of re-frame, and monitoring metadata can be hard given that multiple tools just display EDN which does not include metadata. Is there any other way that I'm missing? Any advice would be greatly appreciated.

manutter5117:07:06

That’s an interesting problem. I’m not sure I understand what you mean by a :dispatch effect being executed with a different ws. You mean like when switching workspaces, you get a effect running in the new ws that should have been run under the old one? Or is it like you’re staying in Workspace A, but for some reason you need to run an effect in Workspace B without changing the current workspace?

p-himik17:07:12

@manutter51 I think the former? Let me give an example. Suppose you have an fx event handler for event :start that returns {:http-xhrio {:on-success [:stop], ...}}. :start is dispatched by a button "Start" and :stop shows the ws it has been dispatched with somewhere in the UI. There's also another button that switches current ws. With this series of events - :w1 is selected - "Start" is pressed - :http-xhrio cofx fires a request - :w2 is selected - :stop is fired by :http-xhrio cofx I want :w1 to be displayed in the UI, despite the fact that by the time it's displayed, current ws has been changed to :w2.

manutter5118:07:13

Hmm, we had a similar situation that’s specific to :http-xhrio — we just passed the variable in as the last item of the vector, i.e. {:http-xhrio {:on-success [:stop :w1], ... }}

p-himik18:07:57

Yeah, that's the most obvious solution, and the hardest to implement when ws is needed by almost all subscriptions and event handlers.

p-himik18:07:28

The third way that comes to my mind would be to rely extensively on macros and to use some special function to create an event vector just so that the macros know what's an event and what's not. But this solution is probably worse than the metadata one because it's harder to implement, strays from "event is just data" approach, harder to debug etc.

manutter5118:07:18

I’m not sure I understand why it’s hard to include the current ws in the arg vector?

manutter5118:07:09

Maybe it’s just that the :http-xhrio is an easy case, since you’ve got the whole db ready-at-hand when the event handler is running?

p-himik18:07:05

It's not hard by itself. But imagine you have hunders of event handlers and hunders of subscriptions. In/for each and every one you will have to: 1. Add an optional (since views generally should not care about current ws) ws argument 2. Check that the ws argument is set 3. If not, set it to the current ws stored in app-db 4. For each effect, make sure to pass ws everywhere. Every :dispatch, :dispatch-n, :on-success, :on-complete, :on-god-knows-what-else. And what if the effects map is composed dynamically? Including all event vectors in there.

p-himik18:07:38

It's way more code than required, and it makes it waaay easier to mess things up.

manutter5118:07:41

Ok, I see what you’re saying now

p-himik18:07:04

Another thing to consider - what if I'm using some libraries that have events that use something like :http-xhrio? Only with the metadata solution will I be able to support such ws switching.

manutter5118:07:25

I don’t know if even metadata would work if you had a lib that was calling :http-xhrio for you — you’d need a way to attach the ws to the arg vector for :on-success, and there’d be no way to tell the lib to do that for you.

p-himik18:07:17

Dammit, you're right. Previously I was inadvertently thinking about the metadata in the context of completely synchronous effects.

manutter5118:07:56

Thinking out loud here: what you’re looking for is a way to do a transactional set of effects, such that :start and :stop can belong to the same transaction, which would somehow also preserve the workspace for the entire transaction

p-himik18:07:19

Yeah, pretty much.

manutter5118:07:13

I keep thinking of something like a stack, where :start would push the current ws and :stop would pop it off again. But that would be possibly difficult to do in an async setting

p-himik18:07:37

You mean a stack per transaction? But how would it help, if even the concept of transactions is not that clear here. If it's a global stack, then how would it help with transactions?

manutter5118:07:32

Yeah, I’m kind of throwing out ideas right now to see if I can come up with anything useful

manutter5118:07:40

I was thinking a global stack, or maybe some kind of map, like “I’m storing the state for transaction #134” and then the rest of the transaction could look it up

manutter5118:07:59

That’s probably a more complicated solution than the problem we’re trying to solve tho 😕

p-himik18:07:37

I think that instead of metadata it's also possible to use the context. But it doesn't really change anything, just another implementation. It's still not possible to use this with some thirdparty libraries, but it should be easy enough to create a thin wrapper for them.

manutter5118:07:39

One other thought I had was maybe a with-ws function

manutter5118:07:01

so like {:on-success (with-ws [:stop])}

p-himik18:07:24

It's interesting how re-frame makes many things really neat and makes other things rather complicated to implement while staying within the re-frame approach. How would such a function work?

manutter5118:07:56

What I would probably do is have a map as the 2nd value in the vector, like {:on-success [:stop {}]}

manutter5118:07:47

so if you need to pass extra arguments to :stop, you’d add them to the map. Then with-ws could insert the current ws in as a key like :current-ws inside that map

manutter5118:07:34

I like maps because it’s real easy to set up optional arguments that way. Then you could have a matching get-ws function that looks for that key, and just returns the current ws if the key isn’t set

p-himik18:07:30

It would work, and I definitely agree on maps, but it's not that different from manually passing ws everywhere.

p-himik18:07:09

Have you ever looked at pure-frame?

manutter5118:07:18

no, I haven’t

p-himik18:07:50

Its approach was to make everything pure - so, no global app-db, you have to pass everything around.

manutter5118:07:59

Heh, interesting

manutter5118:07:12

chocolatier does something like that for a game engine

manutter5118:07:18

Oh, I remember what the other part of the with-ws idea was — you write a custom dispatch function to wrap re-frame.core/dispatch and apply the with-ws to the arg vector

manutter5118:07:20

If you’ve got a well-defined convention of having a map as the last value in the vector you’d simplify things a bit

p-himik18:07:33

As far as I recall, the author eventually decided that it was too much of a hassle to use such a framework. Aah, that's more like it. But it won't handle :dispatch and :dispatch-n, so you'll still have to reimplement a good chunk of re-frame. 🙂

manutter5118:07:57

Yeah, I was thinking the same sort of thing about :on-success. But still, you could do similar: have :my/dispatch and :my/dispatch-n effects

manutter5118:07:52

It wouldn’t be too hard to write the effect handlers, and it might be worth the investment if you had to scale it across hundreds of events/subs

p-himik18:07:44

That's true. Ok, I really have to go to sleep now - almost 2AM here. Thank you very much for the conversation! It'll definitely help. Hopefully, I'll come up with something in the morning. Have a good day!

manutter5118:07:54

Good luck! 🙂