Fork me on GitHub

Merry Christmas / happy holidays, dear Fulcro community! Hope you're off being merry somewhere and not reading this 😁 Let's say I have a button which when pressed changes the route of the Fulcro router. On the target I have a deferred routing handler which will load what's necessary before completing the route. Now, say I want to do the same load on hovering the button, as a kind of optimistic pre-load. How do I detect there's a pre-load in progress in the :will-enter handler of the router target, avoid issuing two identical loads and route correctly? I can check the marker when :will-enter is first called and defer, but then how do I route once the preload completes? I can send target-ready in my hover load, and it works, but then I get if I don't end up clicking the button. I'm guessing there's a better way, but I've not been able to find it or come up with it. (Setting a watch on the state atom in :will-enter is the best I could come up with.)

Jakub Holý (HolyJak)20:12:29

Nice question! Went so you think you wouldn't see it in the will enter? You have access to app and can use that to query / look into the state. Alternatively, you could make a custom mutation that sets / checks and resets a loading? flash before issuing the load. But df/load! with a marker seems simpler...


I had looked at that 🙂 As I understand it, the :will-enter function only gets called once (after click, when the route attempts to get changed), correct? Which means when it gets called I can check the marker. And let's say the marker is :loading, so I need to still defer the route and wait for the previous load to finish. After it finishes, what then? And I don't think I could use the marker within a component to issue a change-route!. Besides doing something custom, as you said, maybe there's a way through state machines? But I haven't yet looked at them...


Maybe as part of the hover load, I can do a custom mutation which would check whether routing is pending and if so complete the route?


The marker is unreliable in this case (timing could through off your check). In this case you want to know that you've already scheduled the load. The load marker may not have been set yet (thought I can't imagine why), but it definitely could have finished before you look. What you want is your own custom marker that indicates you've definitely asked for the make a boolean in state for that express purpose. Loads are implemented as transactions in the underlying system, and you can issue them from mutations. So, make a preload mutation that adds a :ui/preloaded? true to a relevant location that you can check and issues the load. Treat routing as routing: do the deferred bit and decide what you need from the server, using all relevant information (e.g. the preloaded flag).


I'm sorry, I'm not sure I follow 100%. Let me re-state what you've said: Use a custom flag :ui/preloaded? to track only whether the thing has been preloaded (finished loading). Check this flag on :will-enter and act accordingly. Correct? 1. I don't understand how this is different from checking whether a marker is :loading ? If the marker is missing, whether or not I should load in :will-enter is determined by whether or not the target is in state, which is (can be made) the same as checking :ui/preloaded? 2. And I don't get how this would help me avoid issuing duplicate loads? Maybe you're saying this is unavoidable? Sorry if I'm missing something obvious.


> So, make a `preload` mutation that adds a `:ui/preloaded? true`  to a relevant location that you can check and issues the load. If I think about mutations being optimistic, this would indicate you are saying the :ui/preloaded? flag should be (is) set before issuing the preload (hence it tracks whether the preload has been called, not finished loading)... This seems to me to be the same as detecting a :loading marker in the sense that I'm back to not knowing how to complete the route.


If in :will-enter I detect the preload is still in progress, would sending the target-ready mutation with {:optimistic? false} guarantee that mutation will run after the preload finishes? [I wanted to test this, but finally figured out that I'm running Pathom client-side, so the hover load blocks the main thread and my click (`change-route!`) handler gets called only after Pathom finishes loading. In other words, I cannot realise the case I'm trying to solve for in this thread. As I'm probably not going to run Pathom client-side in production, would appreciate figuring this out anyway. What does happen is, since change-route! get's called immediately after, the db hasn't had time to normalize the new data (?), so in :will-enter I don't even detect that the data has been loaded and the marker is missing. That's where Tony's :ui/preloaded? starts making sense.]

Jakub Holý (HolyJak)15:12:15

I would do 1.,ie a custom mutation that sets the custom"loading" flag (:ui/preloaded?). In its ok-action I would check whether routing waits for the given target and if it does then issue dr/target-ready. In will-enter if I detect the custom flag, I return dr/route-deferred without any action, resulting on the custom mutation to send target ready.


Sounds good, thank you! I guess I would prefer not to mix concerns (routing in the preload), so what about this? > If in `:will-enter` I detect the preload is still in progress, would sending the `target-ready` mutation with `{:optimistic? false}` guarantee that mutation will run after the preload finishes?


The only guarantees Fulcro gives is on order. If you queue a mutation and a load while holing the UI thread, then the mutations will go first, and the loads will follow. Among mutations and loads order will be preserved. The Async nature of I/O means that I can make no further promises. So, you need to mark each phase of whatever task you're doing so you can tell where things are at. There are many possibly interleavings with what you're trying to do, and so you want to not have to reason about that non-trivial mess. I would not convolute routin


DR's integrated defer is something you can choose to use if it fits your use-case (what I found to be common). If it doesn't fit, then do something else. You can do immediate routing at the router layer, and simply write your own logic around this more complex feature.


All target-ready does is, at some point in the future, let the router complete its job.


you need to get out of bit of paper and think through the Hover -> Preload started -> Preload finished -> User click to route vs Hover -> Preload started -> User click -> route deferred routine runs -> Preload finishes, etc.


This is why we're suggesting you use some explicit markers so you can more simply delineate things...YOU still have to figure out when you want to tell components to do other things for do the bookkeeping!


Preload started (put something somewhere so you know started), when preload finishes, update the marker so you know that. Now you have declarative facts that will be easy to reason about


You can just do immediate routing with the routing system, and ignore that complexity altogether. Make manually written mutations around these operations that do what you want, and trigger the route when you're ready to show it...and just use route-immediate in will-enter...I would avoid composing async things because they are confusing to reason about when you do that.


Thank you so much, Tony and Jakub! Of course I need to do a more careful analysis and I don't expect to, uhm, defer, thinking to you guys 🙂 As somebody actively learning Fulcro, it's hard to see when failing to come up with a solution is a failure of knowledge, imagination or when it calls for being more creative with what you, Tony, have so generously provided for us. These discussions are immensely helpful, so thank you again!


Welcome. Think of the provided as ons like routing and uism to be just that...optional add ons. The core components, application, data fetch and mutations are the core building blocks. Everything else kind of popped up over time, but should not be considered the only way to do things. They're there because I found them useful.

gratitude 1