Fork me on GitHub

Hello Fulcro lovers. I try to use with Fulcro (without expo) but I have some trouble to register the app. I try this code:

(defn -mount-app []
  (.registerComponent AppRegistry "App" (fn [] (mroot/floating-root-react-class entry/Root app)))
  (let [root-component (interop/react-factory (.-element (.getApplication AppRegistry "App")))]
    (app/mount! app (root-component nil) "app")))
I use .registerComponent of react-native-web to register the main component. Internaly, react-native-web wrap this component with an internal component. The .getApplication return an object with a key element (whish is the react element). I miss something. I got this error on the console Uncaught Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object. Someone already tried to use this lib with Fulcro?

Jakub Holý (HolyJak)10:01:21

I have no experience here but the same error happens in the browser. What exactly threw this error? React or Fulcro? And where, during mount! or earlier? Essentially somewhere you are passing something that should be turnable to a React element and thus needs to be a DOM element such as "div" or a class for a class-based component or a function for a pure, fn-based one. Do you need to go through the React interop factory here? You already have something that perhaps is a React element, the .-element thing. Can't your use it as is?


Hi @U0522TWDA, thanks to answer me. The error is threw by React during the mount!. Yes .-element is a React element but this is a plain JS React element. If I remove the react-factory I got the same error.

Jakub Holý (HolyJak)11:01:58

Makes sense actually since element is not class/fn

Jakub Holý (HolyJak)12:01:46

Forgetting all the React native stuff, if you have a plain React element - whether in native or Web - you should be able to mount it IMO. According to that seems indeed be the case. So I believe that is not render that is failing but something else. The key question is : what?!


Yes but app/mount! can only mount component created by defsc macro no?


I don't know if this is an acceptable code but it works:


It's more of a hack than anything. If you see another way I'm interested.

Jakub Holý (HolyJak)18:01:40

Ah, I see, you are right. There must be a better way, but I don't know it. Perhaps mount! is not really needed? Isn't the element returned by the getApplication already mounted? If not, you could perhaps call instead ReactDOM.render (or its native alternative) directly instead of fulcro's mount? And run separately (app/set-root! app Root {:initialize-state? true}) ? Not sure what is the expected interaction between native and Fulcro here...


No, the element returned by .getApplication isn't mounted. React Native Web offer a .renderApplication for mount the app but I wanted to use mount! fulcro function to correctly setup the mount.

Jakub Holý (HolyJak)19:01:30

Study its code to find out whether it actually does anything you need. I doubt it somehow, if you run set-root. But I'm just guessing...


I will do that. Thanks again @U0522TWDA


I keep using the mount! function with my hack code for the moment because I don't know what to use otherwise.

Jakub Holý (HolyJak)20:01:37

Nice!!! Though I think you miss )) at the end of 🙂 @U0CKQ19AQ Do you have experience with react native? Does ☝️ look good to you?


Thanks @U0522TWDA for the review 🙂

🙏 1

Hello guys! Re update the gist ( with the idea of @U0522TWDA . Don’t use the mount! function of Fulcro. Just set the root component with set-root! it works

🎉 1

Ok for me @U0522TWDA 🙂

🙏 1

Maybe a downside of this method is the Fulcro inspect display No app connected 😕


You can manually ask for inspect to connect




might also have to call install in that ns…don’t remember, but it isn’t tied to react or mount


Thanks for the advice @U0CKQ19AQ


install has to be called, but is usually called from preload


because it only gets called once, but multiple apps can be on a page…each of those sends an app-started


Getting more adventurous with state machines - I like the idea of them being where the logic lives.. I'm playing with a pattern where I run multiple instances of the same machine on a page - multiple objects, each having its own copy of the machine I'm using uism/begin! app machine ident when ident is in the form [::session/session object-id] This is working well but I have hit a small problem I now want to be able to pull the state machine into a component and check it's active state In accordance with the guide, it suggests adding the state machine to the query of the component so that changes in the state machine trigger a re-render but.. how do I construct the query in the component if the ident is "dynamic" I'm using routing so the object-id is in the url and available in :will-enter, not sure if that's going to helpful here...??

Jakub Holý (HolyJak)20:01:32

You could do that with dynamic queries but simpler will be to fetch all state machines (with '_, as you do for load markers) and just look up the relevant one in there. The cons is that change to any uism will rerender all these components but likely you can afford that.

👆 1

I want to implement navigation with two routers named RootRouter (DataList["c"], BigDetail["d" :data/id]) and TabRouter (Pic["pic" :data/id], Txt["txt" :data/id]). So if I want to see Txt of :data/id=2 item the path should be like: d/2/txt/2 I am working on this problem several days, and I am still unable to pass :data/id from BigDatil to Pic. Another problem is that will-enter is triggered 3 times on one click.


I am not sure if passing the :data/id from one component to another is the correct way, From Datalist -> BigDetail it works,


But from BigDetail to Pic or Txt the id is passed, but request from database gets nil


Do you have the code on a public git repo? it's hard to understand what exactly you're asking for, as well as what you're trying to do.


It seems like you already have the fulcro repo locally - the router is a pretty simple state machine so you can add prints and inline defs to debug as well: ready-handler and route-handler manipulate the state on change route


The will-enter is triggered several times. In order to write side-effecting code, use route-deferred and the completion function takes no arguments and should invoke target-ready.

👍 1

@U051V5LLP This is how I pass id from one parent route BigDetail to child Pic Pic receives the param in will-enter, but it is not able to fetch the data from data/id table. But Pic and BigDetail are very very similar, I see the difference only in render fun


You forgot to do (comp/get-initial-state TabRouter) in BigDetail. Might cause a problem as well


Are you suggesting replacing :initial-state {:ui/router {}} with :initial-state (fn [_] {:ui/router (comp/get-initial-state TabRouter)}) They are equivalent.


My bad, I didn't remember it's an empty map as well. I've implemented a lot of master/Detail views in fulcro and just looked at your code for a minute. It looked fine but this line stood out to me. I can try to run you code locally, haven't worked with workspaces yet though, so it might take a minute


@U012ADU90SW with calva is very simple:

[email protected]:janezj/fulcro.git
cd fulcro
git checkout tab-card
yarn install
jack-in .... select; shadow-cljs -> :workspaces


I got the workspaces running. I just noticed that the TabRouter props aren't passed through to the BigDetail component, I don't know why though yet. Have to take a closer look. If you click on txt or pic and force a re-render, it'll switch to the correct tab


How do you force re-render? I noticed it shows correct tab when shadow-cljs remounts ui.

Jakub Holý (HolyJak)20:01:02

In Fulcro Inspect - DB there is a button for it, I think it is a circular arrow


Thanks, I didn't know that. I tried with (app/schedule-render! SPA it is probably the same but button is more convenient

Jakub Holý (HolyJak)20:01:20

> Pic receives the param in will-enter, but it is not able to fetch the data from data/id table. Why/how is it not able to fetch the data? You can run something like db->tree inside the dr/deferred-route callback to get it, no? But why do you need to fetch the data in will-enter? If you have them in query then fulcro will fetch them for you? > But Pic and BigDetail are very very similar, I see the difference only in render fun Then perhaps have a single component with (if <cond> (render-pic ..) (render-detail ...) ? Not sure whether it makes sense here just wanted to point out that you don't need separate components for everything, esp. if they have the same query and props.

Jakub Holý (HolyJak)20:01:21

So all your targets display different views of the same data entity, right? You don't really need routers for that (and I believe they were not designed for this use case - the main advantage of routers is avoiding fetching subtrees of data you don't need). You can more simply implement the same thing with a single component (or two) with a conditional. Instead of passing route params, you can simply set a well-known places in the DB such as :ui/selected-item [:data/id 123] + :ui/current-view {:left :Pic, :right :BigDetail} at the root. And you could even sync that with the URL using custom code (if you don't care about user friendliness then adding ?state=<base64-encoded transit or edn> is a simple way. You can still do it with routers I believe but want to open your mind to other possibilities.


I was not exact, it would be better, But component is not able to fetch the data,


I am building larger application, I just replicated the problem in a card


There are going to me much more tabs/components And i want to load the requred data on will-enter


Every tab will have some other data, imagine for exampke youtube video with id, on every tab, I will have details about video, (comments), technical propertis, tools to share, .... . But all sharing just id.


I thing that pic, just don't have something set correctly, so query is not working

Jakub Holý (HolyJak)20:01:50

> But component is not able to fetch the data, You mean it runs df/load! but it fails or what? > i want to load the requred data on will-enter well, you could instead load them in onClick, just before "routing" to the component/view. So I think it is still valid approach but if you want routers then routers be it! 🙂


no the problems is that Pic is not receiving id in props

Jakub Holý (HolyJak)21:01:44

I am afraid you are asking for troubles since both BigDetail and its grandchildren Pic , Txt use the same ident. Perhaps it is messing up already now. But certainly after you switch over to deferred routing - then both BigDetail's and Pic's will-enter is eventually expected to transact (dr/route-ready [:data/id 2] So how should Fulcro know which of the two targets it is that is ready if both have the same ID? I might be wrong but I suspect strongly this will mess up. Now I I route in the card to D.pic and look at the data in F.Inspect - DB Explorer, I can go from root at :root/router -> [::dr/id ::RootRouter] - :current-route -> [:data/id 2] - which contains various :data/* props but no :ui/router as BigDetail expects so here is your disconnect.

Jakub Holý (HolyJak)21:01:01

From (comp/get-initial-state Root) I see the initial state has no :data/id data. I see the data is added by the merge/merge-component! So I have to point you to the pre-merge info here For some deeper understanding it is valuable to study

Jakub Holý (HolyJak)21:01:53

BTW might have warned you about the missing connection

Jakub Holý (HolyJak)21:01:45

Also under there is the (obviously misplaced) useful point 2.a.i.B


> both `BigDetail` and its grandchildren `Pic` , `Txt` use the same ident. If I understand you correctly: in the hierarchy of routers there must be no duplicated [:data/id x] pairs. target-ready will fail. I agree, I was lookung for trouble. > From `(comp/get-initial-state Root)` I see the initial state has no `:data/id` data. I don't see it as an error, ids of data items are loaded from database in my real app I am using load! This just like example in the book - PeopleList

👍 1

As I understand routing: "targets" in router are not linked to the root. ident in target is just table pointer to query data from.


Thank you. I will change the idents and find another way to pass from parent

Jakub Holý (HolyJak)16:01:58

Routers do no break with basic principles. So targets must be connected to the root (though your problem is that the component containing the router lacked connection to the router data, not anything with targets? )