From 9ebafc46bbb0c649ae9bfe26342e43f612ae765d Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Mon, 6 Feb 2023 00:54:08 -0800 Subject: [PATCH 1/2] feat: allow 'static futures to be spawned from handlers --- examples/button.rs | 9 ++++++++- packages/core/src/events.rs | 21 +++++++++++++++++++++ packages/core/src/lib.rs | 8 ++++---- packages/core/src/scopes.rs | 7 ++++--- packages/html/src/events.rs | 2 +- 5 files changed, 38 insertions(+), 9 deletions(-) diff --git a/examples/button.rs b/examples/button.rs index 9d32819e8..2764f88b0 100644 --- a/examples/button.rs +++ b/examples/button.rs @@ -6,6 +6,13 @@ fn main() { fn app(cx: Scope) -> Element { cx.render(rsx! { - button { "hello, desktop!" } + button { + onclick: |e| async move { + println!("hello, desktop!"); + tokio::time::sleep(std::time::Duration::from_secs(1)).await; + println!("goodbye, desktop!"); + }, + "hello, desktop!" + } }) } diff --git a/packages/core/src/events.rs b/packages/core/src/events.rs index 849dd4d24..b7057fbcc 100644 --- a/packages/core/src/events.rs +++ b/packages/core/src/events.rs @@ -3,6 +3,10 @@ use std::{ rc::Rc, }; +use futures_util::Future; + +use crate::ScopeState; + /// A wrapper around some generic data that handles the event's state /// /// @@ -106,6 +110,23 @@ impl std::fmt::Debug for Event { } } +#[doc(hidden)] +pub trait EventReturn

: Sized { + fn spawn(self, _cx: &ScopeState) {} +} + +impl EventReturn<()> for () {} + +pub struct AsyncMarker; +impl EventReturn for T +where + T: Future + 'static, +{ + fn spawn(self, cx: &ScopeState) { + cx.spawn(self); + } +} + /// The callback type generated by the `rsx!` macro when an `on` field is specified for components. /// /// This makes it possible to pass `move |evt| {}` style closures into components as property fields. diff --git a/packages/core/src/lib.rs b/packages/core/src/lib.rs index 304197dab..4fd8e4a8a 100644 --- a/packages/core/src/lib.rs +++ b/packages/core/src/lib.rs @@ -71,10 +71,10 @@ pub(crate) mod innerlude { pub use crate::innerlude::{ fc_to_builder, AnyValue, Attribute, AttributeValue, BorrowedAttributeValue, CapturedError, - Component, DynamicNode, Element, ElementId, Event, Fragment, IntoDynNode, LazyNodes, Mutation, - Mutations, Properties, RenderReturn, Scope, ScopeId, ScopeState, Scoped, SuspenseContext, - TaskId, Template, TemplateAttribute, TemplateNode, VComponent, VNode, VPlaceholder, VText, - VirtualDom, + Component, DynamicNode, Element, ElementId, Event, EventReturn, Fragment, IntoDynNode, + LazyNodes, Mutation, Mutations, Properties, RenderReturn, Scope, ScopeId, ScopeState, Scoped, + SuspenseContext, TaskId, Template, TemplateAttribute, TemplateNode, VComponent, VNode, + VPlaceholder, VText, VirtualDom, }; /// The purpose of this module is to alleviate imports of many common types diff --git a/packages/core/src/scopes.rs b/packages/core/src/scopes.rs index bee7ea1db..201e1d424 100644 --- a/packages/core/src/scopes.rs +++ b/packages/core/src/scopes.rs @@ -7,7 +7,7 @@ use crate::{ innerlude::{ErrorBoundary, Scheduler, SchedulerMsg}, lazynodes::LazyNodes, nodes::{ComponentReturn, IntoAttributeValue, IntoDynNode, RenderReturn}, - AnyValue, Attribute, AttributeValue, Element, Event, Properties, TaskId, + AnyValue, Attribute, AttributeValue, Element, Event, EventReturn, Properties, TaskId, }; use bumpalo::{boxed::Box as BumpBox, Bump}; use bumpslab::{BumpSlab, Slot}; @@ -581,9 +581,9 @@ impl<'src> ScopeState { /// 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( + pub fn listener>( &'src self, - mut callback: impl FnMut(Event) + 'src, + mut callback: impl FnMut(Event) -> E + 'src, ) -> AttributeValue<'src> { // safety: there's no other way to create a dynamicly-dispatched bump box other than alloc + from-raw // This is the suggested way to build a bumpbox @@ -596,6 +596,7 @@ impl<'src> ScopeState { propagates: event.propagates, data, }) + .spawn(self); } })) }; diff --git a/packages/html/src/events.rs b/packages/html/src/events.rs index d2aec70c1..604c3a955 100644 --- a/packages/html/src/events.rs +++ b/packages/html/src/events.rs @@ -8,7 +8,7 @@ macro_rules! impl_event { ) => { $( $( #[$attr] )* - pub fn $name<'a>(_cx: &'a ::dioxus_core::ScopeState, _f: impl FnMut(::dioxus_core::Event<$data>) + 'a) -> ::dioxus_core::Attribute<'a> { + pub fn $name<'a, E: ::dioxus_core::EventReturn, T>(_cx: &'a ::dioxus_core::ScopeState, _f: impl FnMut(::dioxus_core::Event<$data>) -> E + 'a) -> ::dioxus_core::Attribute<'a> { ::dioxus_core::Attribute { name: stringify!($name), value: _cx.listener(_f), From 14a1039808806ec500c77ceea4ebd2e273c4aeaf Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Mon, 6 Feb 2023 22:18:07 -0800 Subject: [PATCH 2/2] make clippy happy --- examples/button.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/button.rs b/examples/button.rs index 2764f88b0..f001ed531 100644 --- a/examples/button.rs +++ b/examples/button.rs @@ -7,7 +7,7 @@ fn main() { fn app(cx: Scope) -> Element { cx.render(rsx! { button { - onclick: |e| async move { + onclick: |_| async move { println!("hello, desktop!"); tokio::time::sleep(std::time::Duration::from_secs(1)).await; println!("goodbye, desktop!");