From 598c59b9c2cc88e44935d9d009a0f8fb61e9f045 Mon Sep 17 00:00:00 2001 From: Greg Johnston Date: Tue, 25 Jun 2024 20:42:20 -0400 Subject: [PATCH] make RemoveEventHandler a concrete type --- tachys/src/html/event.rs | 8 ++++---- tachys/src/renderer/dom.rs | 14 +++++++------- tachys/src/renderer/mock_dom.rs | 6 +++--- tachys/src/renderer/mod.rs | 18 +++++++++++++++++- 4 files changed, 31 insertions(+), 15 deletions(-) diff --git a/tachys/src/html/event.rs b/tachys/src/html/event.rs index 38145d456..faae70582 100644 --- a/tachys/src/html/event.rs +++ b/tachys/src/html/event.rs @@ -93,13 +93,13 @@ impl From for Targeted { } } -pub fn on(event: E, cb: F) -> On +pub fn on(event: E, cb: F) -> On where F: FnMut(E::EventType) + 'static, E: EventDescriptor + Send + 'static, E::EventType: 'static, - R: DomRenderer, E::EventType: From, + R: DomRenderer, { On { event, @@ -212,7 +212,7 @@ where { const MIN_LENGTH: usize = 0; // a function that can be called once to remove the event listener - type State = (R::Element, Option>); + type State = (R::Element, Option>); type Cloneable = On, R>; type CloneableOwned = On, R>; @@ -247,7 +247,7 @@ where fn rebuild(self, state: &mut Self::State) { let (el, prev_cleanup) = state; if let Some(prev) = prev_cleanup.take() { - prev(el); + (prev.into_inner())(el); } *prev_cleanup = Some(self.attach(el)); } diff --git a/tachys/src/renderer/dom.rs b/tachys/src/renderer/dom.rs index 7ee3793f0..e671a35ab 100644 --- a/tachys/src/renderer/dom.rs +++ b/tachys/src/renderer/dom.rs @@ -1,4 +1,4 @@ -use super::{CastFrom, DomRenderer, Renderer}; +use super::{CastFrom, DomRenderer, RemoveEventHandler, Renderer}; use crate::{ dom::{document, window}, ok_or_debug, or_debug, @@ -125,7 +125,7 @@ impl DomRenderer for Dom { el: &Self::Element, name: &str, cb: Box, - ) -> Box { + ) -> RemoveEventHandler { let cb = wasm_bindgen::closure::Closure::wrap(cb); let name = intern(name); or_debug!( @@ -138,10 +138,10 @@ impl DomRenderer for Dom { ); // return the remover - Box::new({ + RemoveEventHandler::new({ let name = name.to_owned(); let cb = send_wrapper::SendWrapper::new(cb); - move |el| { + move |el: &Self::Element| { or_debug!( el.remove_event_listener_with_callback( intern(&name), @@ -171,7 +171,7 @@ impl DomRenderer for Dom { name: Cow<'static, str>, delegation_key: Cow<'static, str>, cb: Box, - ) -> Box { + ) -> RemoveEventHandler { let cb = Closure::wrap(cb); let key = intern(&delegation_key); or_debug!( @@ -249,10 +249,10 @@ impl DomRenderer for Dom { }); // return the remover - Box::new({ + RemoveEventHandler::new({ let key = key.to_owned(); let cb = send_wrapper::SendWrapper::new(cb); - move |el| { + move |el: &Self::Element| { drop(cb.take()); or_debug!( js_sys::Reflect::delete_property( diff --git a/tachys/src/renderer/mock_dom.rs b/tachys/src/renderer/mock_dom.rs index aae27ce0f..ecdddcc4b 100644 --- a/tachys/src/renderer/mock_dom.rs +++ b/tachys/src/renderer/mock_dom.rs @@ -4,7 +4,7 @@ //! //! Do not use this for anything real. -use super::{CastFrom, DomRenderer, Renderer}; +use super::{CastFrom, DomRenderer, RemoveEventHandler, Renderer}; use crate::{ html::element::{CreateElement, ElementType}, view::Mountable, @@ -225,7 +225,7 @@ impl DomRenderer for MockDom { el: &Self::Element, name: &str, cb: Box, - ) -> Box { + ) -> RemoveEventHandler { todo!() } @@ -234,7 +234,7 @@ impl DomRenderer for MockDom { name: Cow<'static, str>, delegation_key: Cow<'static, str>, cb: Box, - ) -> Box { + ) -> RemoveEventHandler { todo!() } diff --git a/tachys/src/renderer/mod.rs b/tachys/src/renderer/mod.rs index 79f535f3d..7ac1e6267 100644 --- a/tachys/src/renderer/mod.rs +++ b/tachys/src/renderer/mod.rs @@ -127,7 +127,23 @@ pub trait Renderer: Send + Sized + Debug + 'static { } /// A function that can be called to remove an event handler from an element after it has been added. -pub type RemoveEventHandler = Box; +#[must_use = "This will invalidate the event handler when it is dropped. You \ + should store it in some other data structure to clean it up \ + later to avoid dropping it immediately, or leak it with \ + std::mem::forget() to never drop it."] +pub struct RemoveEventHandler(Box); + +impl RemoveEventHandler { + /// Creates a new container with a function that will be called when it is dropped. + pub(crate) fn new(remove: impl FnOnce(&T) + Send + Sync + 'static) -> Self { + Self(Box::new(remove)) + } + + pub(crate) fn into_inner(self) -> Box { + self.0 + } +} + /// Additional rendering behavior that applies only to DOM nodes. pub trait DomRenderer: Renderer { /// Generic event type, from which any specific event can be converted.