This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2020-06-01
Channels
- # announcements (2)
- # babashka (10)
- # beginners (133)
- # calva (28)
- # cestmeetup (1)
- # chlorine-clover (31)
- # cider (21)
- # clj-kondo (29)
- # cljs-dev (252)
- # clojure (60)
- # clojure-europe (24)
- # clojure-nl (3)
- # clojure-spec (13)
- # clojure-uk (17)
- # clojurescript (47)
- # conjure (20)
- # datascript (2)
- # datomic (4)
- # figwheel-main (4)
- # fulcro (71)
- # helix (16)
- # jobs (1)
- # meander (56)
- # mount (1)
- # off-topic (15)
- # pathom (25)
- # re-frame (17)
- # reagent (5)
- # remote-jobs (1)
- # shadow-cljs (92)
- # sql (10)
- # tools-deps (71)
- # xtdb (14)
it took me awhile to figure out, so it might help if it was explicitly written somewhere https://clojurians.slack.com/archives/C68M60S4F/p1591001710281000?thread_ts=1590933761.263300&channel=C68M60S4F&message_ts=1591001710.281000
This feels like a big problem.. You can not really google problems you have with fulcro, because you can find most of the solutions only in slack. And slack doesn’t even save them for long … There are only five Questions on SO with the tag “fulcro” and 19 overall in the search..
actually problems come up not that often and Fulcro has great documentation and its source code is waaay more straightforward than of most JS frameworks that’s more of a Clojure “problem” in general, but people are getting used to reading docs and sources and asking on Slack (and argue that that’s more productive than googling, which I agree with)
also REPL helps a lot in resolving such issues, which ain’t feasible in most other ecosystems
@fjolne.yngling this is not a true statement
your problem @mroerni is that you probably don't have any state in that node in the graph.
the order of the query may make some strange difference in the case when you have not reified your graph properly, but it isn't meant to be a "supported" use-case that you have to order it to get it to work.
The problem is that the query will not descend into a component that has no explicit edge in the data graph. If you only query for some "other component's state" and have none of your own, then it will not work. This is an unfortunate consequence of reified views: if it isn't reified (in the data model), then it isn't there....but technically you can still call a function to render it. I have yet to think of a great way to solve that in a general way. You can use "floating roots" if you want to disconnect two sub-trees, but each node still has to have reified data.
TL;DR: Add :initial-state {}
to your component, and make sure the parent composees initial state to itself.
This could be it. The node is under a router. It has an :initial-state
-
But the issue resolved itself… It somehow it started to behave like expected.
and the dynamic queries (used by the routers) also need initialization with a reload.
@tony.kay hm, does that mean that the target is supposed to be a singleton? I have many targets which naturally doesn’t have initial state, and dynamic routing works fine for them except for the first render with dr/current-route
on their parent, which is actually not about being the first, but about dr/current-route
lagging behind in time (so that timeout would fix it)
The target does not have to be a singleton, no…but such things are loaded, and you do have to be careful about execution order in that case.
so, no initial state in that case, but they must not be your initial route in that case
i.e. they should not be first in the list of targets on a router that is in the initial “route”
current-route has to use the reified graph and queries to figure out what is “on screen”…so it is time-sensitive
yep, the issue is in this line, but you have fallback to the first query child which fixes it https://github.com/fulcrologic/fulcro/blob/develop/src/main/com/fulcrologic/fulcro/routing/dynamic_routing.cljc#L292
In the case above, the thing to note is that nothing was in the query except an ident…a case that commonly hurts ppl because they forget to give that node state of its own
ah, I see. Correct, if there is not a component on-screen, then we have to assume that the route is “going to be” the default one listed in the router
It’s been a while since I wrote that code, and it may not be the ideal way of answering the query. Using the on-screen index isn’t ideal in this case, and it would probably be better to use the dynamic query and current state. There will always be, however, timing issues around changing a route and what you “see” from this function call to ask “where are we?“.
That said: dynamic router has some rough edges still, and deserves more attention. I just do not have the time. It could use:
• Better rendering logic to prevent route flickering. The routers should probably auto-coordinate to prevent anything from changing until everything is stable. This is kinda hard, and might even be the wrong thing to “always do”…since routers in the path might want fine-grained control.
• More helpers, like pending-route
to go with current-route
The RAD helpers add some useful stuff, but I’ll probably leave those in RAD.
well, to be honest, I’m just not sure why router should care about what’s on screen and what’s not, and why it couldn’t be just a function of its state machine. I often fallback to legacy router because of its simplicity (especially when I don’t have natural URL segments for targets), dynamic router makes it really hard to reason about in more complex scenarios
I would agree that the simplicity of the union-based routers is nice. Dynamic router exists because ppl kept complaining about union being too hard (i.e. you had to write all the stuff that dynamic routers is doing). So, honestly, dynamic routers was originally meant to be more of a guide to how you’d do routing with dynamic queries and add in things like “deferred routing with the ability to abort”, which is a commonly-needed thing. I avoiding writing that for a very long time just because I didn’t want to really maintain all of that…but no one ever stepped up to write something for the community, and I eventually wanted something for myself. So, “not sure why a router should care”…well, the router doesn’t care, but the actual lifecycle of the whole thing makes it a more complicated scenario. Once you add in the idea that a route can be “in progress” (e.g. due to a slow load) and might be cancelled (i.e. a user picks a different route before the prior one is “in effect) means that you now want a state machine, and have a fair amount to reason about. That said: if you have more limited requirements you are always welcome to implement your own, open-source it or not, etc. The composition and features of dynamic-router have come in super handy for building the quite feature-rich routing system for RAD, but that doesn’t mean it couldn’t use a touch-up here or there 🙂
yep, I see how dynamic router features are advanced ones, which is certainly a part of the complexity, and that’s great to have it in general (so thank you 🙂 ), just a few thoughts come to mind: • as I see it, “in progress”, deferred routes, etc don’t actually require target instance per se, those are things that should be taken care of by the state machine, as you already mentioned • it seems like the most vital source of complexity comes not from those features, but from the original idea of dynamic routers: separating parent from targets, which allows to refactor targets more easily I’m genuinely amazed by this latter idea, but it proved to be more difficult for scenarios like custom route authentication, query params, etc. So I’ve been thinking, what if route-segments were separated from components, so that routing would be done in a more conventional way: to have a static route tree where nodes refer to components and have their on metadata (auth, etc), and delegate the routing part itself to bidi/reitit That’s not like I’m asking you to make it, of course 🙂 I was going to do it myself for a looong time already… hope to get down to it eventually, so what do you think?
whatever you feel like doing. Dynamic router’s composition, self healing under refactoring, and no need to maintain an “extra config” are all quite important to me. I’m not in agreement that it “proved more difficult” than your ideas, but you’re welcome to implement whatever you want, of course. This is my hope: people will open-source options. I think the result in RAD I’m getting from the features of D.R. is quite impressive, personally. Not sure why you think it is, per say, a blocker for any of the features you’ve mentioned. I’m doing it on my own projects with D.R., and it really isn’t that hard. You have a sequence to manage, and that is going to be touchy no matter what you use.
in fact, RAD has things like “passing extra params” from one generated to component working…the route parameters are extensible, and in RAD I’ve got full EDN encoding to the URL going for every possible parameter on each screen….sort order of tables, which row was highlighted, which page you’re on in a paginated result, what sort order, etc. And it is easy to use code to route to a particular view with any of those params.
so, personally, I’m very very happy with how D.R. is working in general…but it does have a rough edge here or there…nothing that can’t be fixed, but they’re tolerable problems (to me) for now.
It is tempting, however, to just drop the deferred routing feature. It sounded like a good idea, but in hindsight I think the user should just manage that concern themself. Then, there’d be no need for a SM in the router, and it could just be synchronous. That would simplify it greatly, I think, but allowing routes to at least “advise” against routing away is something I’d want to keep.
it certainly ain’t blocker, just more difficult to reason about for me. I have both auth and query params with D.R. now, but in my implementation it just doesn’t look quite clean to say the least... I played a bit with RAD, but it’s already massive, so I put its research till better times with fewer deadlines :) I kinda appreciate deferred routing in the sense that it allows for data fetching co-located with the component itself, and that’s what I need most of the time (not complex UISM driven logic), but not in the sense that it is deferred ahah: I’d like it switch route ASAP and show loader in place of the target
thank you for taking time on this discussion, think I should probably first look into RAD’s implementation, chances are I’m missing something but even in the case of going down the rabbit hole with “static” routing I‘ll certainly try to open source it... though stewardship is hard, better no library than bad library ahah
Is it generally supported to have a subcomponent use a root link query? I'm seeing a parent component get the data properly using: {[:all-users '_] (comp/get-query User)}
but rendering a nested list component {:user/list (comp/get-query UserList)}
which does the same link join receives no data
thanks. I have the state map to support the nested link. The issue I'm seeing is the first render of data works fine. I'm clearing the data after the user logs out and then after a user logs in again the data is loaded and put in the top level of the state map but the link query nested component gets no data
i'm running the same link query in the parent and receiving data at render time, but the child component using the same link query receives nil
yes, that’s because a component with link query should have proper own state http://book.fulcrologic.com/#_a_warning_about_ident_and_link_queries
ok, so as far as I understand you use children only for rendering, they don’t have natural state of their own? in this case it seems better to just pass them data from the parent, and have them defined as common React components (not Fulcro ones): (defsc Child [this props] (dom/div ,,,))
it might even make sense to define them as regular functions if they’re simple enough (i.e. don’t require props optimizations)
That would work, and what I might end up doing - I just wanted a List component that I could drop in around the app without needing to wire up the list of data it needs
I think i found a solution, - was just playing around with what is proper "empty state" for the subcomponent when the data is reset
i think that did it- i had to set the join property on the parent to {}
to get the query working again
on login:
set the prop to the nested prop to empty map:
[:component/id :my-thing :my-list]
{}
and insert the top level list of idents that the nested component queries for
thank you @fjolne.yngling rereading that section made me think about what the query resolver is actually doing
i have a related general question - what i'd really like is run initial-state for my app again for a logout + login flow. I don't see a way to do this other than hand curating the state db to some sane initial state. is there a way to issue a "re-run initial state" mutation for the entire app?
@danvingo ah, I thought you were trying to link query from List items, not from the List component itself. if you’re link querying from the List itself, then you don’t really need to set up its state on every mutation: your mutation works because your List doesn’t have an ident, thus its state isn’t normalized and stored inside every component it turns out to be a child of. Something like this should work:
(defsc List [this {:link/keys [data]}]
{:query [[:link/data '_]]
:ident (fn [] [:component/id :my/list])
:initial-state {}}
,,,)
(def ui-list (comp/factory List))
(defsc Parent [this {:parent/keys [list]}]
{:query [{:parent/list (comp/get-query List)}]
:ident (fn [] [:component/id :my/parent])
:initial-state {:parent/list {}}}
(ui-list list))
but i'm still wondering what's the best practice around resetting a db after an auth reset?
having to build up the state db out of band from the components is not great, that's what get-initial-state is supposed to handle
@danvingo of course you can reset state: (reset! (::app/state-atom app) (comp/get-initial-state Root {}))
and doing that in a mutation is even easier, as long as you know the root (which you can get from the app…so)