shadow-cljs 2025-04-18

I am experiencing an issue with hot reloading. I have a map of tab-names and components that looks like this:

(def component-spec
  [{:tab :users
    :component users/root}
    {:tab :email
     :component email/root}])
In a separate namespace, these components are passed along and called via into:
(into [section-component] component-args)
Changes made to users/root are are not being hot reloaded in the UI. This wasn’t an issue before updating React 16 to React 18 && shadow-cljs “2.23.3” to “2.28.20" Were there any known changes that may have caused this?

I don't recommend using vars since they generate a bunch of "noise" I wouldn't want in a release build. the anon functions achieve the same goal and will just be removed by :advanced, so no loss there

👍 1

Sometimes a combination of a factory function and a more dynamic reference like #'users/root has got me unstuck with reloading

this has nothing to do with the shadow-cljs version and will have worked the exact same since hot-reload was added

so, hot-reload swaps the actual var at email/root. not the reference in the map, that holds on to whatever it was when the map was created

now if for whatever reason that map is NOT recreated by the hot-reload it will keep using the old reference

the default hot-reload mechanism only reloads namespaces that actually changed and then namespaces that directly depended on those that changed, not further

so depending on how things are structured this may not catch everything and have lingering references floating arround the system

what may have changed in react is that it decide to skip rendering early, since its arguments didn't change. as in the old reference is still the old reference rendering the old thing

basically the fix is to NOT hold onto other vars directly

what you can do is add anonymous functions, so instead of :component users/root you pass :component #(users/root %) (or however many args are passed to that component

this anonymous function can remain unchanged, but when called will look up and call the latest version of that var

react not rendering you'll have to sort for yourself, this is not something shadow-cljs has any hand in

best option is to just stick with data 😛 so only use {:tab :users} {:tab :email} and in the component rendering the stuff just do a (case tab :users (users/root ...) :email (email/root ...) ...). that will also never suffer any lingering references to old stuff

1

FWIW there is also :devtools {:reload-strategy :full} in the build config, which is kind of the nuclear option to always reload everything

will slow down hot reloads substantially, but usually fixes all sort of intermediate state

^that was the only thing that worked, but it didn’t seem ideal

I will try this stuff out - thank you so much for the response

It turns out the fix was the components (let’s call one Z for simplicity) were rendered in another namespace (let’s call it Y) in a component rendered by another namespace (let’s call it X) So the requires for Z and it’s sister components needed to be present in X (not Y). I was stuck on this for a long time, just wanted to pass along the info to anyone who might get use out of it.

🎉 1

so, the problem was react deciding it didn't need to re-render because Z didn't change (because shadow-cljs didn't recompile it)

adding the require adds it to the files that get recompiled. this isn't ideal because you are basically doing a slower version of :reload-strategy :full. as in force an actual recompiled, instead of just a reload 😛

^ that did work. It is a rather large application however, and it was a little painful so I’m glad to have found this fix 🙂

oh, you’re saying this is slower?? lol

💯 1

I'd expect so? I mean otherwise it just reloads the file, now it compiles and reloads it

so far it feels faster, but maybe I’m high on my own solution

best option is to make react just re-render the thing in some way