Fork me on GitHub

@mdhaney I’ve had the exact same thought. I certainly want to ease the transition as much as possible. There are some challenges to the compat library approach, but I think it is mostly doable. However, one problem with using the old nses is that you may stomp on your ability to use libraries like workspaces, because it aims to support both, and is written in 2 (both can co-exist in a browser tab/project, but you cannot cross up the APIs…i.e. an F2 app and F3 app could both be mounted on a page, and you could even mount one or the other on a DOM node created by the other, but you cannot compose an F3 component as part of an F2 app and expect anything to work). If you tried to use a compat lib and workspaces, things will almost certainly break in ways you cannot fix (the two versions of defsc produce dramatically different things, for example). The other challenge is the change of namespaces of well-known keywords, like the form state config join key. Fortunately the docs have you use some pre-defined vars for most of those (data fetch marker table, form state config being the two most common), so that may alleviate the porting issue. It may be possible to make a ns-compatible wrapper for F3 that works pretty well, but until I stabilize the source base for F3 it is staying mostly in the back of my mind, and should probably not be worked on much (unless you enjoy frustration).


I’ve also considered writing some kind of automation tool that could modify your source code for the items that are easy (renaming nses), and output a list of linter-like changes that need to be made…not sure how easy that will be either.


The other thing I’m going to try to do is port as many of the misc functions random support functions as possible, so that most of the porting really should be fixing namespaces. However, if you’re using anything I’ve tossed out (e.g. the modular server, easy server, server-side defmutation/defquery-root/defquery-entity); however, it is trivial for you to pull most of that into your own code base and just not have a dep on f2 for it.


So, I hope to make porting mostly a matter of: 1. If you were using the server stuff beyond handle-api-request you’ll either need a compat library for that (which I will probably provide), or you’ll need to port to pathom. 2. Change a bunch of namespaces. 3. Change how you construct your client app. 4. Possibly adapt some of your network activity code (the information is still going to be there, but I’m simplifying what is available). The incubator stuff is not going to be usable with F3, but the important stuff is part of F3 now. So, if you’re using UISM or dyn routers you’re fine. If you’re using the pmutate from incubator you may have a little work to do, but the new mutation system in F3 should be easily amenable to writing your own defmutation macro that exactly emulates that system. There is some chance I’ll expand the default mutation macro to support a bit more of what that system provides, though the current key names and such in env are not really compatible, at least at the moment.


The fact that the new mutation system allows you to intercept the raw result from the network layer means that the plumbing is much more amenable to user customization.


(See default-result-action in the book and fulcro-app docstring.)


Anyway, as soon as I feel like I’ve got it mostly ready, I’ll port a more significant app to it and try to alleviate the pain as much as possible.


One of the things that is taking me so long on rewriting the book is that I’ll get to a chapter and go “Oh yeah, I didn’t write that yet”. The nice part of that is that at least I have decent documentation on what everything is supposed to do 😜

😂 4

along with live examples that either work or they don’t

💯 4

Can gnl/ghostwheel.stubs {:mvn/version "0.4.1"} be on "main" :deps to facilitate the use of fulcro as a git dep?


The problem is that I need to be able to toggle which of those in on classpath for release builds vs dev, as will anyone that uses it. Officially a Fulcro 3 requirement will be that you either use one or the other of those (stubs or not).

👍 4

As long as one or the other is on your classpath it should work fine as a git dep…though IntelliJ might not highlight correctly in fulcro source files in that circumstance I think.

👍 4

I tried to bump this project from e01a3a6f036e6844e350a8606e89911aa2eb51e3 (16/Jun) to e1e637e398dc38466b6a175f0dd0e6d223d17c57 (08/Jul) But when I call mount! it throws:

Uncaught TypeError: Cannot read property 'cljs$core$IFn$_invoke$arity$1' of null
    at souenzzo$graph_demo$client$$fulcrologic$fulcro$components$component_did_mount [as componentDidMount] (components.cljc:268)
    at commitAllLifeCycles (react-dom.development.js:17335)
    at HTMLUnknownElement.callCallback (react-dom.development.js:150)
    at Object.invokeGuardedCallbackImpl (react-dom.development.js:200)
    at invokeGuardedCallback (react-dom.development.js:257)
    at commitRoot (react-dom.development.js:18949)
    at react-dom.development.js:20419
    at Object.exports.unstable_runWithPriority (scheduler.development.js:256)
    at completeRoot (react-dom.development.js:20418)
    at performWorkOnRoot (react-dom.development.js:20347)


Also be wary of following non-released code. I am renaming some keywords and may break things…I try to test more heavily before a release.


Chances are the specific problem you’re seeing there is that this is required arg now on lifecycle methods?


There is no life-cycle methods at all. But I'm using "legacy union router"


well, I could have broken that router 😕


it isn’t a spec error you printed…it’s an attempt to call an arity 1 function…I’ll have a look


Oh…the ns changed names, and your cache has an old version I think


the old router ns is now legacy_ui_routers


a clean of that project would not even compile against latest SHA


That legacy ns has two kinds of routers in it: the old dynamic router, and the union router. Unfortunately the name “dynamic router” is being used in the new stack to mean the one from Incubator…so I have somewhat of a naming problem. I could split the two old ones out into new nses so the names are accurate (but leave some confusion about dynamic routers), or keep the old things together in a single ns that is just the legacy stuff (which was my original approach, but the name was poor).


Actually, @U2J4FRT2T, I’ve verified that this is a bug in the latest HEAD. Breaks all apps.


fixed in latest commit…was a typo in a keyword rename


nice! running with de942ec74d9f3e95aea90cfdc8a6e27eb81efd5d I'm trying some ssr stuff, but it warns about

WARN [com.fulcrologic.fulcro.algorithms.application-helpers:40] - Attempt to access an undefined app algorithm :props-middleware
WARN [com.fulcrologic.fulcro.components:439] - get-ident returned an invalid ident: [nil nil] nil


the props middleware one likely isn’t a problem…and the other is likely an issue with your app state

👍 4

does it render anything is the real question 😜


ah, there is a problem there. User is technically supposed to consume 2 args, but the first arg is repetitive, and my code partially changed the requirement.


just a sec…


@U2J4FRT2T fixed I think in latest commit. Try again.

👍 4

initial state now takes only one arg, as described in doc string


Working. Now when I run

(let [ui-root (fc/factory Root)]
  (dom/render-to-str (ui-root (fc/get-initial-state Root {}))))
I get
Execution error (ClassCastException) at com.fulcrologic.fulcro.dom_server.ReactText/renderToString (dom_server.clj:178).
class java.lang.Long cannot be cast to class java.lang.StringBuilder (java.lang.Long and java.lang.StringBuilder are in module java.base of loader 'bootstrap')
from [com.fulcrologic.fulcro.dom_server$render_element_BANG_$fn__53795 invoke "dom_server.clj" 356] I'm debugging


(dom/render-to-str (dom/div 
Execution error (ClassCastException) at com.fulcrologic.fulcro.dom_server.ReactText/renderToString (dom_server.clj:178).
class java.lang.Long cannot be cast to class java.lang.StringBuilder (java.lang.Long and java.lang.StringBuilder are in module java.base of loader 'bootstrap')
(dom/render-to-str (dom/div 
=> "<div data-reactroot=\"\" data-reactid=\"1\" data-react-checksum=\"-1738207180\">ok</div>"


@U2J4FRT2T try next commit 🙂


Nice! SSR working!


thanks for sticking with me and reporting all of that


one less thing for me to walk though myself

👍 4

(not really trying to load from JVM, just playing ATM) It should be (fn [& _send] ...


@tony.kay sounds like you’re 3 steps ahead of me in thinking about migration. Well, still happy to help out when you get to that point. Another thing I’ve thought about is the lein new template. IIRC you weren’t planning on maintaining that going forward, but I think it could be worth doing as a community effort, especially if we make it full-featured enough to really help people hit the ground running when starting a project. I’m thinking lots of options to choose what goes in the generated app, like Luminus does. Client, server, or both; workspaces or not; web sockets or http; browser or React Native 😉 - options like that. I have some experience writing lein templates, although it’s been awhile (I did the template to generate a plugin project for Light Table, back when that was a thing). So that’s another area I can help out with.

💯 16
👍 8

Is it acceptable to use fs/add-form-config to :initial-state? The fulcro book doesn’t mention this use.


@levitanong technically it should work, but here’s why that’s a bad idea: initial state is only calculated once, based on all the components reachable from Root when the application starts. If you later refactor your component tree to where that is no longer the case, now your component is broken It doesn’t happen often, but when it does it can be very difficult to track down. It’s tempting to think of initial state as a “constructor” for the component, but that can get you into trouble as mentioned above (technically it’s more like a constructor for the first rendered frame of your app). But since Fulcro 2.8+, there is something more analogous to a component “constructor”: pre-merge. Not only will it be used when constructing initial state, but also whenever you are merging component data into the app later (i.e. loaded from a server). IMO, that’s a much better place to add your form state config.


@mdhaney thanks for the explanation! Does this mean then that the fulcro book should be updated to include the use of pre-merge with fs/add-form-config?


I think it may have mentioned it briefly, not sure. With Fulcro3 pending, I doubt the current docs are getting a lot of updates, but if @tony.kay agrees it might be worth adding an expanded explanation or example in the F3 docs. And I'm sure he would appreciate a pull request for either/both. 😉

👍 4

@mdhaney I just realized though, wouldn’t the concerns about it not working on later component merges be rendered moot if the form is a singleton? i.e. I’m not spawning new forms about specific entities. Instead, there’s just a form that gets reset after every submission


You can certainly add form config in initial state @levitanong…one of the book examples does exactly that


but you have to add it as part of the tree, since init state is a tree, not a db


at least I think one of the examples does…the docstring in the form-state namespace mentions this use-case here:


as per Fulcro conventions, the one to use in mutations has a * suffix:


The former will walk the provided data tree for sub-forms, so, you can compose your children into the data structure of initial state and then call that once on the top form component’s initial state…the pre-merge stuff just generalizes it so it will work with I/O better.


@levitanong it would not hurt to have an example.

👍 4
Thomas Moerman22:07:02

Q: I'm having some trouble with querying with mutual references between components (e.g. Report has many Images, Image has a Report). This leads to following error:

Uncaught RangeError: Maximum call stack size exceeded
at Object.fulcro$client$primitives$get_query_by_id [as get_query_by_id] (primitives.cljc:808)
I assume this is caused by the circular references, is there a recommended way to deal with this? I currently just query without using (prim/get-query Report), i simply query for the id {:image/report [:report/id]} and then manually convert it to a proper ident using a :pre-merge routine. Now, in my case I don't really need it to be a proper ident, I mainly use the report id to filter images by a given report. I guess I'm looking for the idiomatic Fulcro way of dealing with this situation. Any pointers? Thx


@thomasmoerman That is a hard limitation of Fulcro’s need to be able to get the full query statically…it creates an infinite recursion (in code) when you do that. The only “workaround” is to manually build different things out to the query depth, or choose an overall different structure for things. So, ask things like: Is the report in an image the same kind of report as it is contained in? If not, perhaps you really need a diff component. You can also try to model the recursion into a single component, and use pathom to merge the relevant details at the server layer so that the recursion is all in a single UI componet.


In other words, simething like ReportWithImage where the image is optional


then you can use Fulcro’s query recursion to go out to arbitrary depth


(defsc ReportWithImage [_ _]
  {:query [:image/url {:report/sub-report '...}]})


the alternative is to manually code a Report1, Image1, Report2, Image2, etc…and use those to do your manual query depth. Then use some common function in the body of them to share rendering.


(defn Report [this props]
  {:query [{:report/image (get-query Image)}]
  (render-report this props))

(defn Image [this props]
  {:query [{:image/report (get-query Report1)}]
  (render-image this props))

(defn Report1 [this props]
  {:query [{:report/image (get-query Image1)}]
  (render-report this props))


it’s a little tedious, but totally tractable