Create closure type; allow async event handlers in props; allow short hand event handlers (#2437)
* create closure type; allow async event handlers in props; allow shorthand event handlers * test forwarding event handlers with the shorthand syntax * fix clippy * fix imports in spawn async doctest
This commit is contained in:
1 parent
79e18c2d62
commit
d795995e20
11 files changed
+294
-103
No files matched your search
@@ -520,7 +520,7 @@ mod struct_info {
|
||||
empty_type, empty_type_tuple, expr_to_single_string, make_punctuated_single,
|
||||
modify_types_generics_hack, path_to_single_string, strip_raw_ident_prefix, type_tuple,
|
||||
};
|
||||
use super::{child_owned_type, looks_like_event_handler_type, looks_like_signal_type};
|
||||
use super::{child_owned_type, looks_like_callback_type, looks_like_signal_type};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct StructInfo<'a> {
|
||||
@@ -634,12 +634,12 @@ mod struct_info {
|
||||
|
||||
let event_handlers_fields: Vec<_> = self
|
||||
.included_fields()
|
||||
.filter(|f| looks_like_event_handler_type(f.ty))
|
||||
.filter(|f| looks_like_callback_type(f.ty))
|
||||
.collect();
|
||||
|
||||
let regular_fields: Vec<_> = self
|
||||
.included_fields()
|
||||
.filter(|f| !looks_like_signal_type(f.ty) && !looks_like_event_handler_type(f.ty))
|
||||
.filter(|f| !looks_like_signal_type(f.ty) && !looks_like_callback_type(f.ty))
|
||||
.map(|f| {
|
||||
let name = f.name;
|
||||
quote!(#name)
|
||||
@@ -1593,7 +1593,7 @@ Finally, call `.build()` to create the instance of `{name}`.
|
||||
}
|
||||
|
||||
/// A helper function for paring types with a single generic argument.
|
||||
fn extract_base_type_without_single_generic(ty: &Type) -> Option<syn::Path> {
|
||||
fn extract_base_type_without_generics(ty: &Type) -> Option<syn::Path> {
|
||||
let Type::Path(ty) = ty else {
|
||||
return None;
|
||||
};
|
||||
@@ -1670,11 +1670,11 @@ fn remove_option_wrapper(type_: Type) -> Type {
|
||||
|
||||
/// Check if a type should be owned by the child component after conversion
|
||||
fn child_owned_type(ty: &Type) -> bool {
|
||||
looks_like_signal_type(ty) || looks_like_event_handler_type(ty)
|
||||
looks_like_signal_type(ty) || looks_like_callback_type(ty)
|
||||
}
|
||||
|
||||
fn looks_like_signal_type(ty: &Type) -> bool {
|
||||
match extract_base_type_without_single_generic(ty) {
|
||||
match extract_base_type_without_generics(ty) {
|
||||
Some(path_without_generics) => {
|
||||
path_without_generics == parse_quote!(dioxus_core::prelude::ReadOnlySignal)
|
||||
|| path_without_generics == parse_quote!(prelude::ReadOnlySignal)
|
||||
@@ -1684,13 +1684,16 @@ fn looks_like_signal_type(ty: &Type) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
fn looks_like_event_handler_type(ty: &Type) -> bool {
|
||||
fn looks_like_callback_type(ty: &Type) -> bool {
|
||||
let type_without_option = remove_option_wrapper(ty.clone());
|
||||
match extract_base_type_without_single_generic(&type_without_option) {
|
||||
match extract_base_type_without_generics(&type_without_option) {
|
||||
Some(path_without_generics) => {
|
||||
path_without_generics == parse_quote!(dioxus_core::prelude::EventHandler)
|
||||
|| path_without_generics == parse_quote!(prelude::EventHandler)
|
||||
|| path_without_generics == parse_quote!(EventHandler)
|
||||
|| path_without_generics == parse_quote!(dioxus_core::prelude::Callback)
|
||||
|| path_without_generics == parse_quote!(prelude::Callback)
|
||||
|| path_without_generics == parse_quote!(Callback)
|
||||
}
|
||||
None => false,
|
||||
}
|
||||
@@ -1709,20 +1712,21 @@ fn test_looks_like_type() {
|
||||
ReadOnlySignal<Option<i32>, UnsyncStorage>
|
||||
)));
|
||||
|
||||
assert!(looks_like_event_handler_type(&parse_quote!(
|
||||
assert!(looks_like_callback_type(&parse_quote!(
|
||||
Option<EventHandler>
|
||||
)));
|
||||
assert!(looks_like_event_handler_type(&parse_quote!(
|
||||
assert!(looks_like_callback_type(&parse_quote!(
|
||||
std::option::Option<EventHandler<i32>>
|
||||
)));
|
||||
assert!(looks_like_event_handler_type(&parse_quote!(
|
||||
assert!(looks_like_callback_type(&parse_quote!(
|
||||
Option<EventHandler<MouseEvent>>
|
||||
)));
|
||||
|
||||
assert!(looks_like_event_handler_type(&parse_quote!(
|
||||
EventHandler<i32>
|
||||
)));
|
||||
assert!(looks_like_event_handler_type(&parse_quote!(EventHandler)));
|
||||
assert!(looks_like_callback_type(&parse_quote!(EventHandler<i32>)));
|
||||
assert!(looks_like_callback_type(&parse_quote!(EventHandler)));
|
||||
|
||||
assert!(looks_like_callback_type(&parse_quote!(Callback<i32>)));
|
||||
assert!(looks_like_callback_type(&parse_quote!(Callback<i32, u32>)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
use dioxus::prelude::*;
|
||||
|
||||
// This test just checks that event handlers compile without explicit type annotations
|
||||
// It will not actually run any code
|
||||
#[test]
|
||||
#[allow(unused)]
|
||||
fn event_handlers_compile() {
|
||||
fn app() -> Element {
|
||||
let mut todos = use_signal(String::new);
|
||||
rsx! {
|
||||
input {
|
||||
// Normal event handlers work without explicit type annotations
|
||||
oninput: move |evt| todos.set(evt.value()),
|
||||
}
|
||||
button {
|
||||
// async event handlers work without explicit type annotations
|
||||
onclick: |event| async move {
|
||||
println!("{event:?}");
|
||||
},
|
||||
}
|
||||
|
||||
// New! You can now use async closures for custom event handlers!
|
||||
// This shouldn't require an explicit type annotation
|
||||
TakesEventHandler { onclick: |event| async move {
|
||||
println!("{event:?}");
|
||||
} }
|
||||
// Or you can accept a callback that returns a value
|
||||
// This shouldn't require an explicit type annotation
|
||||
TakesEventHandlerWithArg { double: move |value| (value * 2) as i32 }
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn TakesEventHandler(onclick: EventHandler<MouseEvent>) -> Element {
|
||||
rsx! {
|
||||
button {
|
||||
// You can pass in EventHandlers directly to events
|
||||
onclick: onclick,
|
||||
"Click!"
|
||||
}
|
||||
button {
|
||||
// Or use the shorthand syntax
|
||||
onclick,
|
||||
"Click!"
|
||||
}
|
||||
|
||||
// You should also be able to forward event handlers to other components with the shorthand syntax
|
||||
TakesEventHandler {
|
||||
onclick
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn TakesEventHandlerWithArg(double: Callback<u32, i32>) -> Element {
|
||||
let mut count = use_signal(|| 2);
|
||||
rsx! {
|
||||
button {
|
||||
// Callbacks let you easily inject custom logic into your components
|
||||
onclick: move |_| count.set(double(count()) as u32),
|
||||
"{count}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user