Switch to owned listener type (#4289)

* Switch to owned listener type

* remove html event workaround

* fix event conversion

* keep track of the origin scope

* fix clippy

* fix docs

* Use "ListenerCallback" instead of "ListenerCb"

* Add a small example

---------

Co-authored-by: Jonathan Kelley <jkelleyrtp@gmail.com>
This commit is contained in:
Evan AlmloffandJonathan Kelley authored and GitHub committed 2025-06-18 16:06:52 -07:00
1 parent 3bf8f09c65
commit af6fc3a542
5 files changed
+143 -270

No files matched your search

+13 -44
View File
@@ -1,11 +1,12 @@
use dioxus_core_types::DioxusFormattable;
use crate::events::ListenerCallback;
use crate::innerlude::VProps;
use crate::prelude::RenderError;
use crate::{any_props::BoxedAnyProps, innerlude::ScopeState};
use crate::{arena::ElementId, Element, Event};
use crate::{
innerlude::{ElementRef, EventHandler, MountId},
innerlude::{ElementRef, MountId},
properties::ComponentFunction,
};
use crate::{Properties, ScopeId, VirtualDom};
@@ -157,29 +158,6 @@ impl Default for VNode {
}
}
impl Drop for VNode {
fn drop(&mut self) {
// FIXME:
// TODO:
//
// We have to add this drop *here* because we can't add a drop impl to AttributeValue and
// keep semver compatibility. Adding a drop impl means you can't destructure the value, which
// we need to do for enums.
//
// if dropping this will drop the last vnode (rc count is 1), then we need to drop the listeners
// in this template
if Rc::strong_count(&self.vnode) == 1 {
for attrs in self.vnode.dynamic_attrs.iter() {
for attr in attrs.iter() {
if let AttributeValue::Listener(listener) = &attr.value {
listener.callback.manually_drop();
}
}
}
}
}
}
impl PartialEq for VNode {
fn eq(&self, other: &Self) -> bool {
Rc::ptr_eq(&self.vnode, &other.vnode)
@@ -810,12 +788,7 @@ impl Attribute {
name: self.name,
namespace: self.namespace,
volatile: self.volatile,
value: match &self.value {
AttributeValue::Listener(listener) => {
AttributeValue::Listener(listener.leak_reference().unwrap())
}
value => value.clone(),
},
value: self.value.clone(),
}
}
}
@@ -839,7 +812,7 @@ pub enum AttributeValue {
Bool(bool),
/// A listener, like "onclick"
Listener(ListenerCb),
Listener(ListenerCallback),
/// An arbitrary value that implements PartialEq and is static
Any(Rc<dyn AnyValue>),
@@ -852,16 +825,8 @@ impl AttributeValue {
/// Create a new [`AttributeValue`] with the listener variant from a callback
///
/// The callback must be confined to the lifetime of the ScopeState
pub fn listener<T: 'static>(mut callback: impl FnMut(Event<T>) + 'static) -> AttributeValue {
// TODO: maybe don't use the copy-variant of EventHandler here?
// Maybe, create an Owned variant so we are less likely to run into leaks
AttributeValue::Listener(EventHandler::leak(move |event: Event<dyn Any>| {
let data = event.data.downcast::<T>().unwrap();
callback(Event {
metadata: event.metadata.clone(),
data,
});
}))
pub fn listener<T: 'static>(callback: impl FnMut(Event<T>) + 'static) -> AttributeValue {
AttributeValue::Listener(ListenerCallback::new(callback).erase())
}
/// Create a new [`AttributeValue`] with a value that implements [`AnyValue`]
@@ -870,8 +835,6 @@ impl AttributeValue {
}
}
pub type ListenerCb = EventHandler<Event<dyn Any>>;
impl std::fmt::Debug for AttributeValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
@@ -879,7 +842,7 @@ impl std::fmt::Debug for AttributeValue {
Self::Float(arg0) => f.debug_tuple("Float").field(arg0).finish(),
Self::Int(arg0) => f.debug_tuple("Int").field(arg0).finish(),
Self::Bool(arg0) => f.debug_tuple("Bool").field(arg0).finish(),
Self::Listener(listener) => f.debug_tuple("Listener").field(listener).finish(),
Self::Listener(_) => f.debug_tuple("Listener").finish(),
Self::Any(_) => f.debug_tuple("Any").finish(),
Self::None => write!(f, "None"),
}
@@ -1197,6 +1160,12 @@ impl IntoAttributeValue for Rc<dyn AnyValue> {
}
}
impl<T> IntoAttributeValue for ListenerCallback<T> {
fn into_value(self) -> AttributeValue {
AttributeValue::Listener(self.erase())
}
}
impl<T: IntoAttributeValue> IntoAttributeValue for Option<T> {
fn into_value(self) -> AttributeValue {
match self {