Share your thoughts in the 2024 State of Clojure Survey!

Welcome! Please see the About page for a little more info on how this works.

+1 vote
in ClojureScript by
retagged by

Is there a way to re-render only the affected parts of the app on hot reload?

Figwheel docs says only about re-mounting the whole app (in which case you lose all the local state of components): https://figwheel.org/docs/hot_reloading.html#re-rendering-ui-after-saving-a-file

Haven't found anything on that matter in shadow-cljs docs. All the apps I've seen so far either not re-render on hot reload at all or do the re-mount.

So, what are the obstacles with comparing pre- and post- hot reload VDOMs and updating the DOM based on this diff? Or is it already implemented?

While Figwheel/shadow-cljs are independent of React/VDOM, that's expected that they don't implement such behaviour, but maybe there's some lib or well-defined approach to do so?

2 Answers

+2 votes
by

In fact it's not "re-mounting", but just an update, it doesn't mean that the whole DOM of the app is being replaced. Calling top level render is not different from updating a component, React still performs diff and patch as usual.

by
edited by
Well, probably I should upload some sample repo, but `reagent/render-component` definitely re-renders the whole app for me. Maybe you have a reference project at hand so that I could compare and find what I'm doing wrong?
by
Uploaded the minimal repro: https://github.com/fjolne/reagent-hot-reload

If some text was entered into the input field and then the text of the title was changed, re-rendering on hot-reload will clear the input.
by
edited by
In this case it is expected because the value of the input is not stored anywhere except DOM, so from React's standpoint there was no value in the input.

Correction: it still happens in Reagent, but not the case for plain React. I guess this has something to do with Reagent's own notion of local state.
by
But why is the input field updated at all if it's not in the diff? Also, the same behaviour is when using Reagent atom to hold the data instead of DOM.

Edit: seen correction, thanks for diving in :)
+2 votes
by

In general frameworks such as fulcro or re-frame have well defined rules about state and how to manage it. Use those.

Doing this at the tool level would be way too hard which is why figwheel and shadow-cljs both only handle reloading the code but leave managing state to the actual application. I'm not even sure this can be done at the tool level without completely restricting what you are allowed to do in the code.

by
I heavily rely on re-frame, but in some cases (simple forms, dropdowns expansion state, etc) it seems more concise to use local state rather than produce re-frame event handlers / subscriptions.
by
Also, as was mentioned above, React itself does handle that. So why would it be not possible to just dump a regular VDOM diff between pre- and post- reload states?
by
React itself does not handle this any differently if you apply the same hot-reload approach to pure JS.

The thing that causes things to be "replaced" is that `(identical? old new)` returns false if you re-define a function via hot-reload. So in your example `app` and `form` have "new" definitions so React replaces them. If you just call `(mount!)` from the REPL the `input` state is not touched and "survives" since the actual diff does not replace the actual DOM node.
by
Oh, didn't know the comparison is also made on the functions themselves, that explains a lot, thanks!
...