Fork me on GitHub
#fulcro
<
2021-03-24
>
stuartrexking02:03:53

I’m getting an error when composing routers. I’m following this structure here https://book.fulcrologic.com/#_routers

ERROR [com.fulcrologic.fulcro.routing.dynamic-routing:684] -  You are routing to a router  :router/SettingsRouter whose state was not composed into the app from root. Please check your :initial-state.

Jakub Holý (HolyJak)11:03:21

Also make sure to use the latest Fulcro

stuartrexking02:03:29

What am I doing wrong?

stuartrexking02:03:37

How do I compose from root when dealing with nested routers?

stuartrexking02:03:00

I need to call dr/initialize! on mount.

stuartrexking12:03:52

Clojure 3.4.20 I’m getting an error with dynamic router target

ERROR [com.fulcrologic.fulcro.components:874] -  Props passed to my.fancy.component/ComponentRoot are of the type PersistentVector instead of a map. Perhaps you meant to `map` the component over the props?

Jakub Holý (HolyJak)12:03:28

What it says :-) Props must be a map. Why are you passing in a vector? Hard to help without seeing the code.

stuartrexking13:03:08

I’m not pass props to the target? The docs state “The initial state parameters passed to such a router are forwarded to the first listed router target (if it has initial state).” This is the code

(defsc Target [this props]
  {:query         [:ui/value]
   :ident         (fn [] [:component/id ::target])
   :initial-state {}
   :route-segment ["target"]}
  (div {} "Target"))

(defsc Target2 [this props]
  {:query         [:ui/value]
   :ident         (fn [] [:component/id ::target2])
   :initial-state {}
   :route-segment ["target2"]}
  (div {} "Target2"))

(defrouter Router [this {:keys [route-factory route-props]}]
  {:always-render-body? true
   :router-targets      [Target Target2]}
  (when route-factory
    (route-factory route-props)))

(def ui-router (comp/factory Router))

(defsc RootComponent [this {:ui/keys [router]}]
  {:query         (fn [] [{:ui/router (comp/get-query Router)}
                          (uism/asm-ident ::Router)])
   :initial-state (fn [_] {:ui/router {}})}
  (div {}
    (ul
       (li
        (button {:onClick #(dr/change-route! this ["target"])} "Target"))
      (li
        (button {:onClick #(dr/change-route! this ["target2"])} "Target2"))
      (li
        (div {} "In")
        (ui-router router)))))

stuartrexking13:03:26

Doesn’t do it in 3.4.16

stuartrexking13:03:34

Trying 3.4.19…

stuartrexking13:03:12

Occurs in 3.4.19

stuartrexking13:03:21

Something changed between 17 and 18.

stuartrexking13:03:46

So what am I doing wrong in the above?

stuartrexking13:03:55

@U0522TWDA Would really appreciate if you give the code example above a quick look. It looks perfectly fine to me, so I’m not sure why I’m getting that error.

Jakub Holý (HolyJak)14:03:37

> I’m not pass props to the target Of course you pass props to the target router The problem is that you are passing it a vector instead of map. So look at the data you have. Why is it a vector? What is in it?

Jakub Holý (HolyJak)14:03:55

Also, have you tried fulcro-troubleshooting?

Jakub Holý (HolyJak)14:03:46

Can you replicate the warning with the code you posted above?!

stuartrexking16:03:22

Yes. I get that error with that code.

Jakub Holý (HolyJak)16:03:35

your error says "ComponentRoot" but that is not in your code?! If it is the root component having issues, how did you register it with Fulcro?!

stuartrexking16:03:18

I renamed the components to simplify the original code but I still get that error. Do you get an error if you run that code? It’s basically the same code as from the book.

stuartrexking16:03:51

I can’t simplify that code anymore as an example router.

Jakub Holý (HolyJak)17:03:27

According to the error, the root component is the problem, not the router. Can you use the instructions here https://blog.jakubholy.net/2020/troubleshooting-fulcro/ for db->tree to see what the root props are? You can also js/console.log the root props

Jakub Holý (HolyJak)17:03:02

Your code snippet is missing your fulcro-app and it's initialization

Jakub Holý (HolyJak)17:03:59

I pasted it into the example.ui ns of fulcro-rad-demo, renamed root component to Root, and fixed imports. Seems to work, do not see this warning you have.

stuartrexking22:03:45

I have the initialization code outside of the snippit. I think the issue is that I’m using a union query in a function query in my RootComponent. Function queries don’t support unions.

stuartrexking00:03:57

It’s strange. The router-props are an ident [:component/id :ns/component] to the target. Should they be a map? @U0CKQ19AQ do you have any idea on how I can debug this further?

stuartrexking00:03:33

(defrouter AuthenticatedRouter [this {:keys [route-factory route-props]}]
  {:always-render-body? true
   :router-targets      [DashboardRoot CompanyRoot PersonRoot InvestorRoot]}
  (when route-factory
    (do
      (log/spy route-props)
      (route-factory route-props))))

(def ui-authenticated-router (comp/factory AuthenticatedRouter))
The route-props are an ident.

stuartrexking00:03:53

And the error is

timbre_support.cljs:80 ERROR [com.fulcrologic.fulcro.components:874] -  Props passed to fierce.pyxis.dashboard.ui/DashboardRoot are of the type PersistentVector instead of a map. Perhaps you meant to `map` the component over the props?

tony.kay00:03:50

have to see more of the composition in code

stuartrexking00:03:48

(defsc AuthenticatedRoot [this {::keys   [router]
                                :ui/keys [sidebar mobile-sidebar]}]
  {:query         [::auth-root
                   {:ui/sidebar (comp/get-query sidebar/Sidebar)}
                   {:ui/mobile-sidebar (comp/get-query sidebar/MobileSidebar)}
                   {::router (comp/get-query router/AuthenticatedRouter)}
                   (uism/asm-ident ::router/AuthenticatedRouter)]

   :ident         (fn [] [:component/id ::auth-root])

   :initial-state (fn [_] {:ui/sidebar        {:ui/sub-sidebars [(dashboard-sidebar-items)
                                                                 (discover-sidebar-items)
                                                                 (account-sidebar-items)]
                                               :ui/account      (comp/get-initial-state account-ui/AccountRoot)}
                           :ui/mobile-sidebar {:ui/items                   mobile-main-sidebar
                                               ::account/name              "Kathrin Mutinelli"
                                               ::account/profile-image-url "/images/kathrin-mutinelli.jpg"
                                               ::organisation/name         "Stratico"
                                               :ui/dropdown-open?          false
                                               :ui/open?                   false}
                           ::router           {}})}
  (log/info router)
  (let [active-route (some-> (dr/current-route this this) first keyword)]
    (div {:className "h-screen flex overflow-hidden bg-white"}
      (sidebar/mobile-sidebar (assoc mobile-sidebar :ui/active-route active-route))
      (sidebar/sidebar (assoc sidebar :ui/active-route active-route))
      (router/ui-authenticated-router router))))

(def ui-authenticated-root (comp/factory AuthenticatedRoot))

stuartrexking00:03:23

That logs the router correctly, but when it passes the props through the router as route-props, it’s an ident.

tony.kay00:03:26

so it is not working at all in latest, but works .4 back?

tony.kay00:03:40

not just that it says an error, but that it is actually busted?

tony.kay00:03:56

in that case, a minimal repro case and an issue would be ideal

stuartrexking00:03:56

I don’t think it’s working at all. The logging was added 17

stuartrexking00:03:35

Would I expect an error if it was working? Why would a route target receive an ident rather than props. That doesn’t sound correct.

stuartrexking00:03:46

Routing works, but I get that error on every route change.

stuartrexking01:03:03

It’s the authenticated flow that’s the problem.

tony.kay01:03:09

do not pass an extra param to get-query

tony.kay01:03:19

that disables dynamic query, which the router needs

stuartrexking01:03:41

I’ve pulled those components from various namespaces and was trying different things. I don’t have it in my actual code. The gist now reflects that.

tony.kay01:03:45

Also, that component should prob have an ident, though technically not required. I’m guessing your extra arg to get-query is the problem

stuartrexking01:03:18

It’s not the problem.

stuartrexking01:03:32

Well, I’ve removed it and I still get the error.

tony.kay01:03:31

you’re using fn for initial state, but you’re using the magic notation…that won’t work

tony.kay01:03:17

you have to call get-initial-state just like get-query if you’re using a fn form for initial state

stuartrexking01:03:54

Thanks Tony. Really appreciate it.

tony.kay01:03:58

that fix it?

stuartrexking01:03:34

Just working through it but it looks like it.

Jakub Holý (HolyJak)11:03:59

If the initial-state was wrong then the client DB content was wrong, which

(let [state (app/current-state APP)]
    (com.fulcrologic.fulcro.algorithms.denormalize/db->tree
      (comp/get-query Root) ; or any component
      ;; Starting entity, state itself for Root
      ;; otherwise st. like (get-in state-map [:thing/id 1]):
      state
      state))
would have shown you, now? Also fulcro-troubleshooting might have warned you about missing data in the client DB?

tony.kay15:03:58

It’s easier to analyze that with Inspect DB Explorer, since that lets you click down the exact path from root you’re looking at

stuartrexking12:03:57

my.fancy.component/ComponentRoot is a target

hadils16:03:59

Hi. I am using form-save* without using defsc-form. What is the proper format for to-one and to-many data elements? Is each element an ident?

hadils17:03:03

The answer is yes. Now, how do I insert a new datom in Datomic? I am getting the following error:

:db.error/not-an-entity Unable to resolve entity: [:job/id #uuid "35994e32-533e-4fa1-9acd-292773a6bf70"] in datom [[:job/id #uuid "35994e32-533e-4fa1-9acd-292773a6bf70"] :job/location [:address/id #uuid "ffffffff-ffff-ffff-ffff-000000000300"]]
    cognitect.anomalies/category: :cognitect.anomalies/incorrect
     cognitect.anomalies/message: "Unable to resolve entity: [:job/id #uuid \"35994e32-533e-4fa1-9acd-292773a6bf70\"] in datom [[:job/id #uuid \"35994e32-533e-4fa1-9acd-292773a6bf70\"] :job/location [:address/id #uuid \"ffffffff-ffff-ffff-ffff-000000000300\"]]"
                           datom: [[:job/id #uuid "35994e32-533e-4fa1-9acd-292773a6bf70"]
                                   :job/location
                                   [:address/id #uuid "ffffffff-ffff-ffff-ffff-000000000300"]]
                        db/error: :db.error/not-an-entity
                          entity: [:job/id #uuid "35994e32-533e-4fa1-9acd-292773a6bf70"]
I think the error is caused by the fact that ]:job/id #uuid"..."] does not exist in the database yet.

Jakub Holý (HolyJak)18:03:49

Are you inserting an entity and a dependent entity in one transaction and D doesn't understand you have both? I guess I can only refer you to Datomic docs. Or look what the RAD datomic save middleware does..

hadils18:03:20

I am creating a new entity and then inserting dependent entities. I guess I need two calls to form-save* , one for the key and one for the remaining fieilds...

hadils20:03:16

Resolved. Replaced a UUID with a TempID.

🎉 3
mss20:03:47

I’m calling transact! inside of a initLocalState declaration that doesn’t seem to be firing the mutation or erroring in any obvious way. I assume there’s a subtle binding issue with the this inside of initLocalState? any ideas what I might be doing wrong?

Aleksander Rendtslev23:03:26

how are you passing this? initLocalState takes a function where the first parameter is this. If you try to pass it the this from the component definition it won’t work (I tripped over that)

mss23:03:09

I mean this is rebound inside of initLocalState, no? so it would be referring to that this

Aleksander Rendtslev23:03:47

I think it's more a matter of that function not having access to the outer "component or app thing". So you need to use the reference passed as the parameter to the function (eg this)

tony.kay04:03:13

initLocalState should not be used this way

tony.kay04:03:02

you can use it in normal lifecycle methods…but initLocalState is sort of mixed into the low-level js constructor.

tony.kay04:03:58

I/O, in general, should not, IMO, be tied to component lifecycle, either. Your data model and logic should drive the UI, not the other way around (the exception being a user-event which is obviously given to you from the UI). When you couple your data lifecycle with your UI component lifecycle you’re asking for trouble (or at least a lot of I/O you don’t need).

tony.kay04:03:36

So, for example, a UI event triggers a route change. A route change calls will-enter (not a UI lifecycle, but a routing lifecycle…subtle but important difference). That, in turn, could trigger something on a UISM that is managing the overall mechanisms of your UI. This is basically how RAD does it: a form is just UI and event triggering on a UISM, where the overall life of the form is managed in logic/data.

tony.kay04:03:06

Another way of saying it: tie your transactions and I/O to user driven things whenever possible. The user activated a route is different than some component on that route “appeared”.

tony.kay04:03:35

they happen very closely in time, but are modeled in code very differently.

mss18:04:18

just realized I never responded to this. thanks @U01DH13SK8E for the help trying to diagnose and @U0CKQ19AQ for the detailed, super informative response – really can’t thank you enough for the time you’ve put into building and maintaining fulcro

🙌 3