diff --git a/packages/core/src/any_props.rs b/packages/core/src/any_props.rs index 1ee0d0ebd..8046868bb 100644 --- a/packages/core/src/any_props.rs +++ b/packages/core/src/any_props.rs @@ -1,5 +1,5 @@ -use crate::{nodes::RenderReturn, Element}; -use std::{any::Any, ops::Deref, panic::AssertUnwindSafe}; +use crate::{nodes::RenderReturn, properties::HasProps}; +use std::{any::Any, ops::Deref, panic::AssertUnwindSafe, rc::Rc}; /// A boxed version of AnyProps that can be cloned pub(crate) struct BoxedAnyProps { @@ -38,22 +38,22 @@ pub(crate) trait AnyProps { fn duplicate(&self) -> Box; } -pub(crate) struct VProps

{ - pub render_fn: fn(P) -> Element, +pub(crate) struct VProps { + pub render_fn: Rc>, pub memo: fn(&P, &P) -> bool, pub props: P, pub name: &'static str, } -impl

VProps

{ +impl VProps { pub(crate) fn new( - render_fn: fn(P) -> Element, + render_fn: impl HasProps + 'static, memo: fn(&P, &P) -> bool, props: P, name: &'static str, ) -> Self { Self { - render_fn, + render_fn: Rc::new(render_fn), memo, props, name, @@ -61,7 +61,7 @@ impl

VProps

{ } } -impl AnyProps for VProps

{ +impl AnyProps for VProps { fn memoize(&self, other: &dyn Any) -> bool { match other.downcast_ref::

() { Some(other) => (self.memo)(&self.props, other), @@ -76,7 +76,7 @@ impl AnyProps for VProps

{ fn render(&self) -> RenderReturn { let res = std::panic::catch_unwind(AssertUnwindSafe(move || { // Call the render function directly - (self.render_fn)(self.props.clone()) + self.render_fn.call(self.props.clone()) })); match res { @@ -92,7 +92,7 @@ impl AnyProps for VProps

{ fn duplicate(&self) -> Box { Box::new(Self { - render_fn: self.render_fn, + render_fn: self.render_fn.clone(), memo: self.memo, props: self.props.clone(), name: self.name, diff --git a/packages/core/src/global_context.rs b/packages/core/src/global_context.rs index bca2f22d0..05002281c 100644 --- a/packages/core/src/global_context.rs +++ b/packages/core/src/global_context.rs @@ -102,8 +102,8 @@ pub fn once(initializer: impl FnOnce() -> State) -> Stat /// Get the current render since the inception of this component /// /// This can be used as a helpful diagnostic when debugging hooks/renders, etc -pub fn generation() -> Option { - with_current_scope(|cx| Some(cx.generation())).expect("to be in a dioxus runtime") +pub fn generation() -> usize { + with_current_scope(|cx| cx.generation()).expect("to be in a dioxus runtime") } /// Get the parent of the current scope if it exists diff --git a/packages/core/src/nodes.rs b/packages/core/src/nodes.rs index 72b03995b..8a0fb4823 100644 --- a/packages/core/src/nodes.rs +++ b/packages/core/src/nodes.rs @@ -1,5 +1,6 @@ use crate::any_props::{BoxedAnyProps, VProps}; use crate::innerlude::{ElementRef, EventHandler, MountId, ScopeState}; +use crate::properties::HasProps; use crate::{arena::ElementId, Element, Event}; use crate::{Properties, VirtualDom}; use std::ops::Deref; @@ -443,7 +444,7 @@ pub struct VComponent { /// The function pointer of the component, known at compile time /// /// It is possible that components get folded at compile time, so these shouldn't be really used as a key - pub(crate) render_fn: *const (), + pub(crate) render_fn: TypeId, pub(crate) props: BoxedAnyProps, } @@ -463,16 +464,21 @@ impl VComponent { /// fn(Scope) -> Element; /// async fn(Scope>) -> Element; /// ``` - pub fn new

(component: fn(P) -> Element, props: P, fn_name: &'static str) -> Self + pub fn new + 'static, P: 'static>( + component: F, + props: F::Props, + fn_name: &'static str, + ) -> Self where // The properties must be valid until the next bump frame - P: Properties, + F::Props: Properties + 'static, { - let vcomp = VProps::new(component, P::memoize, props, fn_name); + let render_fn_id = TypeId::of::(); + let vcomp = VProps::new(component, F::Props::memoize, props, fn_name); VComponent { name: fn_name, - render_fn: component as *const (), + render_fn: render_fn_id, props: BoxedAnyProps::new(vcomp), } } diff --git a/packages/core/src/properties.rs b/packages/core/src/properties.rs index a7ec3e741..62736679c 100644 --- a/packages/core/src/properties.rs +++ b/packages/core/src/properties.rs @@ -67,25 +67,36 @@ impl EmptyBuilder { /// This utility function launches the builder method so rsx! and html! macros can use the typed-builder pattern /// to initialize a component's props. -pub fn fc_to_builder, P>( - _: F, -) -> <>::Props as Properties>::Builder { +pub fn fc_to_builder, P>(_: F) -> <>::Props as Properties>::Builder +where + F::Props: Properties, +{ F::Props::builder() } /// A function pointer that can be used to create a component. pub trait HasProps

{ - type Props: Properties; + type Props: 'static; + + fn call(&self, props: Self::Props) -> Element; } -impl Element> HasProps<(T,)> for F { +impl Element> HasProps<(T,)> for F { type Props = T; + + fn call(&self, props: T) -> Element { + self(props) + } } #[doc(hidden)] pub struct ZeroElementMarker; impl Element> HasProps for F { type Props = (); + + fn call(&self, _: ()) -> Element { + self() + } } #[test] diff --git a/packages/core/tests/context_api.rs b/packages/core/tests/context_api.rs index 59e425bc4..7bfc03836 100644 --- a/packages/core/tests/context_api.rs +++ b/packages/core/tests/context_api.rs @@ -4,7 +4,7 @@ use dioxus::prelude::*; #[test] fn state_shares() { fn app() -> Element { - cx.provide_context(generation() as i32); + cx.provide_context(generation().unwrap() as i32); render!(child_1 {}) }