fix: maintain send/syncness of the handle

This commit is contained in:
Greg Johnston
2025-09-21 14:11:04 -04:00
parent d642806e5e
commit f97fd557c3
4 changed files with 35 additions and 29 deletions

View File

@@ -324,7 +324,7 @@ where
let (el, prev_cleanup) = state; let (el, prev_cleanup) = state;
if let Some(prev) = prev_cleanup.take() { if let Some(prev) = prev_cleanup.take() {
if let Some(remove) = prev.into_inner() { if let Some(remove) = prev.into_inner() {
remove(el); remove();
} }
} }
*prev_cleanup = Some(if E::CAPTURE { *prev_cleanup = Some(if E::CAPTURE {

View File

@@ -259,7 +259,7 @@ where
if let Some(prev) = prev_cleanup.take() { if let Some(prev) = prev_cleanup.take() {
if let Some(remove) = prev.into_inner() { if let Some(remove) = prev.into_inner() {
remove(el); remove();
} }
} }
*prev_cleanup = Some(self.attach(el)); *prev_cleanup = Some(self.attach(el));

View File

@@ -294,21 +294,22 @@ impl Dom {
); );
// return the remover // return the remover
RemoveEventHandler::new(el.clone(), { RemoveEventHandler::new({
let name = name.to_owned(); let name = name.to_owned();
let el = el.clone();
// safe to construct this here, because it will only run in the browser // 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 // so it will always be accessed or dropped from the main thread
let cb = send_wrapper::SendWrapper::new(cb); let cb = send_wrapper::SendWrapper::new(move || {
move |el: &Element| {
or_debug!( or_debug!(
el.remove_event_listener_with_callback( el.remove_event_listener_with_callback(
intern(&name), intern(&name),
cb.as_ref().unchecked_ref() cb.as_ref().unchecked_ref()
), ),
el, &el,
"removeEventListener" "removeEventListener"
) )
} });
move || cb()
}) })
} }
@@ -332,22 +333,23 @@ impl Dom {
); );
// return the remover // return the remover
RemoveEventHandler::new(el.clone(), { RemoveEventHandler::new({
let name = name.to_owned(); let name = name.to_owned();
let el = el.clone();
// safe to construct this here, because it will only run in the browser // 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 // so it will always be accessed or dropped from the main thread
let cb = send_wrapper::SendWrapper::new(cb); let cb = send_wrapper::SendWrapper::new(move || {
move |el: &Element| {
or_debug!( or_debug!(
el.remove_event_listener_with_callback_and_bool( el.remove_event_listener_with_callback_and_bool(
intern(&name), intern(&name),
cb.as_ref().unchecked_ref(), cb.as_ref().unchecked_ref(),
true true
), ),
el, &el,
"removeEventListener" "removeEventListener"
) )
} });
move || cb()
}) })
} }
@@ -446,19 +448,21 @@ impl Dom {
}); });
// return the remover // return the remover
RemoveEventHandler::new(el.clone(), { RemoveEventHandler::new({
let key = key.to_owned(); let key = key.to_owned();
let el = el.clone();
// safe to construct this here, because it will only run in the browser // 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 // so it will always be accessed or dropped from the main thread
let cb = send_wrapper::SendWrapper::new(cb); let el_cb = send_wrapper::SendWrapper::new((el, cb));
move |el: &Element| { move || {
drop(cb.take()); let (el, cb) = el_cb.take();
drop(cb);
or_debug!( or_debug!(
js_sys::Reflect::delete_property( js_sys::Reflect::delete_property(
el, &el,
&JsValue::from_str(&key) &JsValue::from_str(&key)
), ),
el, &el,
"delete property" "delete property"
); );
} }

View File

@@ -1,5 +1,5 @@
use crate::view::{Mountable, ToTemplate}; use crate::view::{Mountable, ToTemplate};
use std::{borrow::Cow, fmt::Debug}; use std::{borrow::Cow, fmt::Debug, marker::PhantomData};
use wasm_bindgen::JsValue; use wasm_bindgen::JsValue;
/// A DOM renderer. /// A DOM renderer.
@@ -121,29 +121,31 @@ pub trait Renderer: Send + Sized + Debug + 'static {
later to avoid dropping it immediately, or leak it with \ later to avoid dropping it immediately, or leak it with \
std::mem::forget() to never drop it."] std::mem::forget() to never drop it."]
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
pub struct RemoveEventHandler<T>(T, Option<Box<dyn FnOnce(&T) + Send + Sync>>); pub struct RemoveEventHandler<T>(
Option<Box<dyn FnOnce() + Send + Sync>>,
// only here to keep the generic, removing which would be a breaking change
// TODO remove generic in 0.9
PhantomData<fn() -> T>,
);
impl<T> RemoveEventHandler<T> { impl<T> RemoveEventHandler<T> {
/// Creates a new container with a function that will be called when it is dropped. /// Creates a new container with a function that will be called when it is dropped.
pub(crate) fn new( pub(crate) fn new(remove: impl FnOnce() + Send + Sync + 'static) -> Self {
el: T, Self(Some(Box::new(remove)), PhantomData)
remove: impl FnOnce(&T) + Send + Sync + 'static,
) -> Self {
Self(el, Some(Box::new(remove)))
} }
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
pub(crate) fn into_inner( pub(crate) fn into_inner(
mut self, mut self,
) -> Option<Box<dyn FnOnce(&T) + Send + Sync>> { ) -> Option<Box<dyn FnOnce() + Send + Sync>> {
self.1.take() self.0.take()
} }
} }
impl<T> Drop for RemoveEventHandler<T> { impl<T> Drop for RemoveEventHandler<T> {
fn drop(&mut self) { fn drop(&mut self) {
if let Some(cb) = self.1.take() { if let Some(cb) = self.0.take() {
cb(&self.0) cb()
} }
} }
} }