I make some component X with some param represented as Y. In X's body I have a ui factory that I pass Y into so I can build the data. In general, I would suspect that if Y is null when it's not supposed to be, the cause would be either that its parent isn't calling it right, or the query is incorrect.
I suspect you’re mental model of Fulcro isn’t complete. A lot of people think Fulcro does more “Magic” for you than it does.
The steps are literally:
On mount:
a. It pulls your initial-state
tree (which is a literal representation of your view tree data).
b. Uses your query, which has the exact same shape as initial-state
, to normalize that tree (there are a few more steps, but that’s basically it).
c. It then denormalizes that tree via the same query…it literally uses your query, from root to get the props tree. Nearly the exact opposite of (b).
d. It calls render and passes that tree of data to your root component.
That’s it.
On each transaction there is a lot of interesting full-stack stuff going on in terms of making your life more sane (serial processing by default, writes prioritized over reads etc etc), BUT when a transaction has modified the database, Fulcro:
a. Gets your root query (which can be dynamic, but usually isn’t)
b. Uses that query + db to get UI data tree
c. Passes it to root render.
Which is very similar to what it does on mount.
That is again, it. Rinse, repeat.
If you use an alt renderer like ident-optimized or multi-root, then there are some extra features.
So you see the model is very very simple:
Db change -> denormalize using Root query + db -> Render root -> Loop
Don’t confuse yourself thinking there’s this magic part of the query or this magic part of the subtree rendering, or that all sorts of weird side-magic is going on, etc. The mental model is: There is a UI tree, there is a data tree. They must match. The render is a visual reification of the data tree using your render.
The query engine in 99% of apps is doing nothing more than following and idents (the main complications are recursion, dynamic queries, wildcards, and unions, which many apps do not even use, and are really nothing very complicated).
To get Fulcro “right”, 90% of the time, is to get the shape of your data graph to match the shape of the query (which initial state makes trivial), and then keep it that way (which is up to your application’s logic).
load
is exactly the initial state+query mechanism, on some sub-graph. You’re making a new part of the tree match some UI that you are about to show.
yeah I've struggled at grasping the data pattern for some time, at least when it comes to troubleshooting. stuff like this helps though, so thanks for taking the time to write it! I wish there was an faq page or something with explanations like these. I suspect there's a lot problem/solution/insight topics that get lost in the archives here which might help connect some of the dots.
The chapter I just revived in the book (3) might help. But I agree, some more docs never hurt
@ajsnow2012 I'm (slowly) grabbing them and putting them on this doc. https://roamresearch.com#/app/CommunityFulcro/page/12-26-2020
if it's not that the function is being called wrong, and if it happens when loading in data, I would then want to test it against static data.
Is there a way to make a catch-all route in the dynamic router? Say I want to have routes /main
/settings
and /:username
, not /user/:username
.
And how do you handle parameters in URL, like /:username?all-posts=true
?
The curse of too much documentation. I could easily add 100+ pages to the book covering these kinds of little details that are in docstrings.
Look at the docstring of the route fn, it takes extra params. Or/and look at how RAD reports use it
I suspect that might be a problem.
I'd look into the function doing the target selection based on the route
I have tried adding the catch-all in the end of the defrouter's routes vector, this didn't work (all routes were going to the catch-all then), but it works if I add it at the front.
So just add the catch-all as the first route in the router, seems like it tries routes from the end.
Is the only way to get the route params of the current route in a component is to receive them in the :will-enter function, and return a deferred mutation/load with those params? I am trying to understand how the person/id prop gets passed into the person component in the router example in the book: https://book.fulcrologic.com/#DynamicRouter Is it because the :person/id key is both in the query and the route-segment, it gets exctracted form the route segment and put into the props?
What about the first page load? I am not using SSR.
I can live with the delay between when the component is rendered and when it learns its route params, just curious.
Hmm, though not sure why the component gets rendered before the path param is loaded if I call dr/target-ready in the mutation after the swap! on state that adds the param.
so, the ideal pattern for a production-ready first page load, IMO, is to have the html render something that shows a loader. The app loads with a flag that is false in initial state (:ui/ready? for example). The rendering when that flag is false is exactly what is already in the app div the HTML generated. So, the user doesn’t really see the transition from static HTML to running app. You do your initialization, route, and then some aspect of your final initialization (which might be route-deferred) changes the flag to true. Now your app renders, and you’re right where you want to be. You are making a frame-by-frame animation via state. Fulcro does not know what you want to show when. It just renders each frame as it happens. You’re ultimately in control of what renders. Dynamic routing has a central set of concerns: composable UI switches that let you have some control over what happens as things come and go from the screen. Nothing’s perfect, and there are certainly imperfections in D.R. I initially wrote that because ppl were complaining they didn’t know how to go about making a ui router, and so I built that as an example, but it was good enough that I released it. It’s since gone through some revision, but it still isn’t ideal in every way. Fulcro’s rendering, as I’ve said at least 3x this week alone, is very very naive: mutation -> render -> mutation -> render. Component’s don’t have much control over when they render, only what. The state machines behind D.R. essentially run mutations to move from state to state, meaning that some (unintentional) frames of state might be rendered…this is where some refinement in the internals would be nice.
That said, my expectation is that if you return a route-deferred
then that route will not be visible until you say target-ready
. The route params are passed in before you can even say route-deferred, so I honestly don’t know what you’re talking about, unless I’m misunderstanding you’re description, or your code doesn’t match your description.
Stepping through state transitions with fulcro inspect can be a good way to gain insight into what is happening in your app.
You mean dr/change-route!
?
(it is in the docstring there and solves the problem, thank you, just making sure)
DR has no concept of URLs. Query parameters can be augmented, and can be placed in vector of strings. DR is not tied to the web. It works for native, electron, or whatever other target you might want. HTML5 “interpretation” is up to you
When you change routes you can put anything in the route params to pass through
The first one is the default, used I suppose when nothing else matches. Surest is to read the code.
After quite an extended break, I published another post in my gift list series detailing how to deploy a fulcro app using dokku: https://chrisodonnell.dev/posts/giftlist/deployment/. Always happy to hear any feedback.
Care to send a PR to add it to https://github.com/holyjak/awesome-fulcro?
Yeah sure
I believe the dr/target-ready
mutation sets its ident.
And dr/route-deferred
, too, I guess.
There's some explanation in this section: https://book.fulcrologic.com/#_deferred_routing