So I'm using error boundaries together with hot code reloading when developing with Reagent, and I've noticed that 'triggered' error boundary often don't get refreshed when my code gets reloaded. Is there a way to force them to get re-rendered on reloads?
FWIW I've tried sprinkling calls to force-update-all
in my 'on-reload' fn, but to no avail:
(reagent.dom/force-update-all)
(reagent.dom/render [views/app-root] (.getElementById js/document "app"))
(reagent.dom/force-update-all)
Nice! Yeah, now reading your messages, I realize that I probably have the same problem. And it's just that I've added an "Undo" button to the re-frame app specifically to undo whatever action has led to the error that prevented me from noticing it.
How exactly does the error boundary code look like?
(defn <error-boundary>
[& _]
(let [*info (r/atom nil)]
(r/create-class
{:constructor (fn [this _props]
(set! (.-state this) #js {:error nil}))
:component-did-catch (fn [_this _e info] (reset! *info info))
:display-name "error-boundary"
:get-derived-state-from-error (fn [error] #js {:error error})
:render (fn [this]
(r/as-element
(if-let [error (.. this -state -error)]
[<error-message>
error
(some-> @*info .-componentStack)]
(into [:<>] (r/children this)))))})))
I don't see anything necessarily wrong with it, but try this one. It works perfectly in my case.
(defn component []
(let [error (reagent/atom nil)]
(reagent/create-class
{:component-did-catch
(fn [_this _e _info])
:get-derived-state-from-error
(fn [e]
(reset! error e)
#js {})
:reagent-render
(fn []
(if @error
[error-component @error]
[main-component]))})))
will do
yeah, no go 😞
I'm using shadow-cljs btw... although I don't think this should affect this sort of thing?
Hmm. And how exactly do you re-render the app on code reload.
I use shadow-cljs as well.
I have a fn that looks like
(defn ^:dev/after-load re-init
"(Re)initializes the state. Called on page load and code reload."
[]
(log/debug "Re-initing...")
(effect-handlers/init!)
(routes/init!)
(sub-handlers/init!)
(reagent.dom/force-update-all)
(reagent.dom/render [views/app-root] (.getElementById js/document "app"))
(reagent.dom/force-update-all)
(css/init-static-classes!)
(set-config!))
wait a second
I think I see something weird in my shadow-cljs config
yeah okay fixing that thing didn't help
my shadow-cljs config is basically
{...
:build {:app {...
:modules {:base {:init-fn site.core/init
:entries [site.core]}}
:devtools {:after-load site.core/re-init
:loader-mode :script
:preloads [shadow.remote.runtime.cljs.browser]}}}
I just confirmed that init
and re-init
get called on page load, and re-init
gets called on reload
weirdly I have no problems with any other elements not getting reloaded
Huh. No clue, sorry.
yeah very strange
I'm gonna take another look at my init code to see if there's anything weird
perhaps I'm getting my re-frame effects mixed up
Now that you mention re-frame
- do you really store *info
in a ratom or was it just to replace some subscription?