React 18 does not live reload with Shadow-CLJS in non-trivial projects
filipesilva opened this issue · 6 comments
Using Reagent+React18+Shadow-CLJS will not live reload unless if the file changed is a direct dependency of the file containing the render code.
I have a repro that illustrates this behaviour:
git clone https://github.com/filipesilva/react18-rerender
cd react18-rerender
yarn
yarn start
Open http://localhost:3001 in your browser and you should see the following:
comp1
comp2
comp3
The browser console will show the following:
render comp1
render comp2
render comp3
Each component is on a different file, app.core/main
will render comp1
, comp1
will render comp2
, and comp2
will render comp3
. Each comp will print to the console when rendered. A render
fn has the ^:dev/after-load
metadata that causes Shadow-CLJS to call it after code changes.
If you edit comp1
or comp2
(e.g. add another number to the string), live reload will work. If you edit comp3
, it will not.
As far as I can tell, the core reason is that the new React 18 rendering fn keeps some state, and will not render the root element unless it changed. In React <17, I think it always rerendered when .render
was called.
Specific to Shadow-CLJS is that it will reload the changed file and all files that directly depend on it, then call the after-load
fn. You can see logs for this in the console when changing comp3
:
shadow-cljs: load JS app/comp3.cljs
shadow-cljs: load JS app/comp2.cljs
shadow-cljs: call app.core/render
Note that the render compX
logs never appeared. In contrast, when editing comp2
:
shadow-cljs: load JS app/comp2.cljs
shadow-cljs: load JS app/comp1.cljs
shadow-cljs: call app.core/render
render comp1
render comp2
render comp3
So far I've found two workarounds for this:
- Render a comp with an anonymous fn (which always changes identity) that returns the comp you really want at root:
(.render root (r/as-element [(fn [] comp1/main)]))
- Mark the ns of the comp you render at root with
^:dev/always
, which causes it to always be reloaded when any file changes.
Unsure if there's any better ways.
hey @filipesilva does your project work fine with React18? any issues except live reloading? thanks
@flexsurfer we had some trouble with other third party js lib and ended up going back to 17. It seemed like reagent was fine with it though.
hey @Deraen , tested latest master , hot reload works fine, any chance we could have a snapshot or release on clojars? thanks
I've been testing my library with React 18, and tried the new create-root
way of rendering a root node, but it seems to break hot reloading in a way similar to what is described in this issue. This is using the latest master
(e67d4f8) branch of Reagent.
Both of @filipesilva's workarounds appear to partially mitigate the issue. But I'm not exactly sure that it is specific to React 18. When using master
(e67d4f8), the old rendering approach with React 17 now also seems to have hot loading issues. Version 1.1.1
of Reagent seems to be fine.
Interestingly, if I revert commit 9211080 on the master
branch, this resolves hot reloading with the old approach for both React 17 and React 18. Of course using the old approach with React 18 will produce the expected warnings and most of the new React 18 features will not be available.
Wrapping the call to react-dom/render
on L35 of reagent.dom
seems to fix the old approach:
(binding [util/*always-update* true]
(react-dom/render
(react/createElement reagent-root #js {:callback callback
:comp comp})
container))
But I still can't get the new create-root
approach to hot reload without the workarounds suggested above.
This report is before 1.2.0 release so I hope this works with the new create-root
wrapper.