fix(hosts,applications): fix wasm-bindgen panic when closing modal
Closing a modal by clicking the backdrop, Cancel, or × called show_modal.set(false) synchronously inside a wasm-bindgen closure. Leptos immediately unmounts the modal, freeing all its closures while the click handler is still on the call stack, which causes wasm-bindgen to panic with "closure invoked after being dropped". Fix: introduce a close() helper that defers set(false) to the next microtask via spawn_local, so the closure returns to wasm-bindgen before the modal is unmounted. Also switch autofocus from Effect+get() to spawn_local+get_untracked() to avoid subscribing NodeRef as a reactive dependency, which would re-trigger during unmount and risk the same panic. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -22,22 +22,33 @@ fn AddApplicationModal(
|
||||
create_action: ServerAction<CreateApplication>,
|
||||
show_modal: RwSignal<bool>,
|
||||
) -> impl IntoView {
|
||||
use leptos::task::spawn_local;
|
||||
|
||||
let name_ref = NodeRef::<Input>::new();
|
||||
|
||||
// Focus the name field as soon as the modal is mounted.
|
||||
Effect::new(move |_| {
|
||||
if let Some(el) = name_ref.get() {
|
||||
// Defer focus to the next microtask so the element is in the DOM.
|
||||
// Using get_untracked() avoids subscribing to NodeRef's reactive signal,
|
||||
// which would otherwise re-trigger during modal unmount and cause
|
||||
// "closure invoked after being dropped" in wasm-bindgen.
|
||||
spawn_local(async move {
|
||||
if let Some(el) = name_ref.get_untracked() {
|
||||
let _ = el.focus();
|
||||
}
|
||||
});
|
||||
|
||||
// close() defers show_modal.set(false) to the next microtask.
|
||||
// Without this, setting the signal synchronously inside a click handler
|
||||
// unmounts the modal (and frees its closures) while the handler is still
|
||||
// on the call stack, causing wasm-bindgen to panic.
|
||||
let close = move || spawn_local(async move { show_modal.set(false) });
|
||||
|
||||
view! {
|
||||
<div class="modal-backdrop" on:click=move |_| show_modal.set(false)>
|
||||
<div class="modal-backdrop" on:click=move |_| close()>
|
||||
<div class="modal" on:click=move |e| e.stop_propagation()>
|
||||
<div class="modal__header">
|
||||
<h2>"Add an application"</h2>
|
||||
<button class="modal__close" type="button" aria-label="Close"
|
||||
on:click=move |_| show_modal.set(false)>
|
||||
on:click=move |_| close()>
|
||||
"×"
|
||||
</button>
|
||||
</div>
|
||||
@@ -58,7 +69,7 @@ fn AddApplicationModal(
|
||||
|
||||
<div class="modal__actions">
|
||||
<button class="btn-secondary" type="button"
|
||||
on:click=move |_| show_modal.set(false)>
|
||||
on:click=move |_| close()>
|
||||
"Cancel"
|
||||
</button>
|
||||
<button type="submit">"Add application"</button>
|
||||
|
||||
@@ -77,17 +77,20 @@ fn AddHostModal(
|
||||
networks_res: LocalResource<Result<Vec<crate::models::Network>, ServerFnError>>,
|
||||
show_modal: RwSignal<bool>,
|
||||
) -> impl IntoView {
|
||||
use leptos::task::spawn_local;
|
||||
|
||||
let name_ref = NodeRef::<Input>::new();
|
||||
|
||||
// Focus the name field as soon as the modal is mounted.
|
||||
Effect::new(move |_| {
|
||||
if let Some(el) = name_ref.get() {
|
||||
spawn_local(async move {
|
||||
if let Some(el) = name_ref.get_untracked() {
|
||||
let _ = el.focus();
|
||||
}
|
||||
});
|
||||
|
||||
let close = move || spawn_local(async move { show_modal.set(false) });
|
||||
|
||||
view! {
|
||||
<div class="modal-backdrop" on:click=move |_| show_modal.set(false)>
|
||||
<div class="modal-backdrop" on:click=move |_| close()>
|
||||
<div class="modal" on:click=move |e| e.stop_propagation()>
|
||||
<div class="modal__header">
|
||||
<h2>"Add a host"</h2>
|
||||
@@ -95,7 +98,7 @@ fn AddHostModal(
|
||||
class="modal__close"
|
||||
type="button"
|
||||
aria-label="Close"
|
||||
on:click=move |_| show_modal.set(false)
|
||||
on:click=move |_| close()
|
||||
>"×"</button>
|
||||
</div>
|
||||
|
||||
@@ -137,7 +140,7 @@ fn AddHostModal(
|
||||
<button
|
||||
class="btn-secondary"
|
||||
type="button"
|
||||
on:click=move |_| show_modal.set(false)
|
||||
on:click=move |_| close()
|
||||
>"Cancel"</button>
|
||||
<button type="submit">"Add host"</button>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user