Fork me on GitHub
Patrick Brown13:06:48

I've got one namespace where I put all my most basic dom elements that are spread across libraries. This is working nice for me and I'd like to keep the pattern. I am trying to move my code from using cljs to cljc. All is going well. This is the offending line in question and error message that's got me tripped up. Any help in what I should do differently. CHEERS! Offending code: (def div dom/div) Relevant stack: #error { :cause "Can't take value of a macro: #'com.fulcrologic.fulcro.dom/div" :via [{:type clojure.lang.Compiler$CompilerException :message "Syntax error compiling at (net/drilling/ui/primative.cljc:40:1)." :data #:clojure.error{:phase :compile-syntax-check, :line 40, :column 1, :source "com/example/ui/primative.cljc"} :at [clojure.lang.Compiler analyze "" 6825]} {:type java.lang.RuntimeException :message "Can't take value of a macro: #'com.fulcrologic.fulcro.dom/div" :at [clojure.lang.Util runtimeException "" 221]}]

Patrick Brown13:06:22

Good stuff @UPWHQK562 ! For those in the future, there is a separate ns for fulcro dom on the server side. [com.fulcrologic.fulcro.dom-server :as dom] Use that and you're golden.

👍 1

@U036UJBDM5G just be careful with performance, if you just make functions that point to functions from fulcro dom you gonna have a performance hit, that's because in Fulcro all the dom things are both macros and fns, when nothing is dynamic (eg: (dom/div {} "foo") the macro is gonna be used, making that translate into a direct call to the React dom fns at compile time, but when things are dynamic, Fulcro falls back to use fn versions that can handle those variables\

👆 1

In practice the performance difference is usually negligible, but I did spend a ton of time getting that all working right in order to get low-level raw react DOM performance.


That said, if you don’t include nil or an empty props map, those optimizations are often not able to be applied 😉

(div (p "hello"))
will be low-level raw react on the p (because a string is obviously not props at runtime), but the div actually will be a run-time function because at compile time it cannot guarantee that the nested body isn’t being called in order to generate props.
(div nil (p "hello"))
will optimize both


I haven't really dug into customizing kondo, but maybe we could have a lint for that?


@UPWHQK562 unfortunately it would highlight about 80% of people’s code, and since it only makes a diff in really heavy pages with lots and lots of elements (like a huge table) I think it would just be a lot of useless noise.


I think we did some performance measurement, and the macro versions are a lot faster; however, most of the real CPU hogging is DOM diffing and modding, so it turns out to be insignificant in most cases due to Amdahls’s Law .


These DOM calls in our code typically take less than 10% of the total time, so even doubling their speed can at most shave off about 5% of a render frame’s overhead.

Patrick Brown21:06:18

On the note of what started this thread... Moving from cljs to cljc was surprisingly easy. I guess it varies from app to app, but I was expecting more work. Doing so has really improved my dev experience. I'm really digging the xplatform approach.


Yeah, I did a lot of work to make sure all of the Fulcro nses work well in CLJC. The DOM one was the only weird one, because of this macro + function stuff. There’s no other way to do that other than to use alternate nses. I personally lean towards making everything in my apps CLJC. I find it makes dev cycle pretty nice, in that I can test code in CLJ (even when its primary target is CLJS). Occasionally the differences pop up, but surprisingly rarely.


Good to hear it was a smooth transition @U036UJBDM5G. I've been thinking about making that move eventually as well. It's somewhat surprising to me to hear that it improves the dx.

Patrick Brown21:06:52

How do people generally handle the http-remote ns with DF/load!?


What do you mean? df is CLJC


you mean how do you make a remote that works in CLJ?


so typically I don’t run the fulcro app itself in CLJ, but if you want to, a remote is just a map that has a function at the :transmit! key. All you have to do is take the transaction it wants run on the “server”, run it on your parser, and then call the correct callback to “return” the result. See mock-http-server (which does this for CLJS apps that don’t want a server…same pattern)

Patrick Brown22:06:19

That looks far more straightforward than I had imagined... But it raises another question, so when you run your app in clj, where do you keep your state if not in the fulcro app client db?


I don’t understand the question. If you run a headless (or SSR) version of your client app in CLJ, it still has a “client side db”…it’s an atom in the app instance.


but for me the whole point of doing that is either SSR (rendering a single frame) or for trying something out where you’re more interested in the state change than the rendering.

Patrick Brown11:06:27

There is a combination of my knowledge of Fucro still being glaringly incomplete and having an atypical use-case/solution. It's working out well, but admittedly my question may show off my ignorance. I appreciate the help. I misunderstood your answer... I think.