From f97fd557c32ebd5d2d385925494d2d37d8c450c1 Mon Sep 17 00:00:00 2001 From: Greg Johnston Date: Sun, 21 Sep 2025 14:11:04 -0400 Subject: [PATCH] fix: maintain send/syncness of the handle --- tachys/src/html/event.rs | 2 +- tachys/src/reactive_graph/bind.rs | 2 +- tachys/src/renderer/dom.rs | 36 +++++++++++++++++-------------- tachys/src/renderer/mod.rs | 24 +++++++++++---------- 4 files changed, 35 insertions(+), 29 deletions(-) diff --git a/tachys/src/html/event.rs b/tachys/src/html/event.rs index f920beb20..8083b5e9f 100644 --- a/tachys/src/html/event.rs +++ b/tachys/src/html/event.rs @@ -324,7 +324,7 @@ where let (el, prev_cleanup) = state; if let Some(prev) = prev_cleanup.take() { if let Some(remove) = prev.into_inner() { - remove(el); + remove(); } } *prev_cleanup = Some(if E::CAPTURE { diff --git a/tachys/src/reactive_graph/bind.rs b/tachys/src/reactive_graph/bind.rs index 60c973a05..7033ce9d1 100644 --- a/tachys/src/reactive_graph/bind.rs +++ b/tachys/src/reactive_graph/bind.rs @@ -259,7 +259,7 @@ where if let Some(prev) = prev_cleanup.take() { if let Some(remove) = prev.into_inner() { - remove(el); + remove(); } } *prev_cleanup = Some(self.attach(el)); diff --git a/tachys/src/renderer/dom.rs b/tachys/src/renderer/dom.rs index 658f5b533..37d8885e5 100644 --- a/tachys/src/renderer/dom.rs +++ b/tachys/src/renderer/dom.rs @@ -294,21 +294,22 @@ impl Dom { ); // return the remover - RemoveEventHandler::new(el.clone(), { + RemoveEventHandler::new({ let name = name.to_owned(); + let el = el.clone(); // safe to construct this here, because it will only run in the browser // so it will always be accessed or dropped from the main thread - let cb = send_wrapper::SendWrapper::new(cb); - move |el: &Element| { + let cb = send_wrapper::SendWrapper::new(move || { or_debug!( el.remove_event_listener_with_callback( intern(&name), cb.as_ref().unchecked_ref() ), - el, + &el, "removeEventListener" ) - } + }); + move || cb() }) } @@ -332,22 +333,23 @@ impl Dom { ); // return the remover - RemoveEventHandler::new(el.clone(), { + RemoveEventHandler::new({ let name = name.to_owned(); + let el = el.clone(); // safe to construct this here, because it will only run in the browser // so it will always be accessed or dropped from the main thread - let cb = send_wrapper::SendWrapper::new(cb); - move |el: &Element| { + let cb = send_wrapper::SendWrapper::new(move || { or_debug!( el.remove_event_listener_with_callback_and_bool( intern(&name), cb.as_ref().unchecked_ref(), true ), - el, + &el, "removeEventListener" ) - } + }); + move || cb() }) } @@ -446,19 +448,21 @@ impl Dom { }); // return the remover - RemoveEventHandler::new(el.clone(), { + RemoveEventHandler::new({ let key = key.to_owned(); + let el = el.clone(); // safe to construct this here, because it will only run in the browser // so it will always be accessed or dropped from the main thread - let cb = send_wrapper::SendWrapper::new(cb); - move |el: &Element| { - drop(cb.take()); + let el_cb = send_wrapper::SendWrapper::new((el, cb)); + move || { + let (el, cb) = el_cb.take(); + drop(cb); or_debug!( js_sys::Reflect::delete_property( - el, + &el, &JsValue::from_str(&key) ), - el, + &el, "delete property" ); } diff --git a/tachys/src/renderer/mod.rs b/tachys/src/renderer/mod.rs index 6ea68cb96..da488116c 100644 --- a/tachys/src/renderer/mod.rs +++ b/tachys/src/renderer/mod.rs @@ -1,5 +1,5 @@ use crate::view::{Mountable, ToTemplate}; -use std::{borrow::Cow, fmt::Debug}; +use std::{borrow::Cow, fmt::Debug, marker::PhantomData}; use wasm_bindgen::JsValue; /// A DOM renderer. @@ -121,29 +121,31 @@ pub trait Renderer: Send + Sized + Debug + 'static { later to avoid dropping it immediately, or leak it with \ std::mem::forget() to never drop it."] #[allow(clippy::type_complexity)] -pub struct RemoveEventHandler(T, Option>); +pub struct RemoveEventHandler( + Option>, + // only here to keep the generic, removing which would be a breaking change + // TODO remove generic in 0.9 + PhantomData T>, +); impl RemoveEventHandler { /// Creates a new container with a function that will be called when it is dropped. - pub(crate) fn new( - el: T, - remove: impl FnOnce(&T) + Send + Sync + 'static, - ) -> Self { - Self(el, Some(Box::new(remove))) + pub(crate) fn new(remove: impl FnOnce() + Send + Sync + 'static) -> Self { + Self(Some(Box::new(remove)), PhantomData) } #[allow(clippy::type_complexity)] pub(crate) fn into_inner( mut self, - ) -> Option> { - self.1.take() + ) -> Option> { + self.0.take() } } impl Drop for RemoveEventHandler { fn drop(&mut self) { - if let Some(cb) = self.1.take() { - cb(&self.0) + if let Some(cb) = self.0.take() { + cb() } } }