Browse Source

fix fullstack send bound

Evan Almloff 1 year ago
parent
commit
499e81fa82

+ 7 - 6
packages/core/src/any_props.rs

@@ -3,8 +3,8 @@ use std::{any::Any, panic::AssertUnwindSafe};
 
 pub(crate) type BoxedAnyProps = Box<dyn AnyProps>;
 
-/// A trait that essentially allows VComponentProps to be used generically
-pub(crate) trait AnyProps {
+/// A trait for a component that can be rendered.
+pub trait AnyProps: 'static {
     fn render(&self) -> RenderReturn;
     fn memoize(&self, other: &dyn Any) -> bool;
     fn props(&self) -> &dyn Any;
@@ -17,17 +17,18 @@ pub(crate) fn new_any_props<F: ComponentFunction<P, M>, P: Clone + 'static, M: '
     memo: fn(&P, &P) -> bool,
     props: P,
     name: &'static str,
-) -> Box<dyn AnyProps> {
-    Box::new(VProps {
+) -> VProps<F, P, M> {
+    VProps {
         render_fn,
         memo,
         props,
         name,
         phantom: std::marker::PhantomData,
-    })
+    }
 }
 
-struct VProps<F: ComponentFunction<P, M>, P, M> {
+/// A component along with the props the component uses to render.
+pub struct VProps<F: ComponentFunction<P, M>, P, M> {
     render_fn: F,
     memo: fn(&P, &P) -> bool,
     props: P,

+ 6 - 5
packages/core/src/lib.rs

@@ -23,6 +23,7 @@ mod tasks;
 mod virtual_dom;
 
 pub(crate) mod innerlude {
+    pub use crate::any_props::*;
     pub use crate::arena::*;
     pub use crate::dirty_scope::*;
     pub use crate::error_boundary::*;
@@ -75,11 +76,11 @@ pub(crate) mod innerlude {
 
 pub use crate::innerlude::{
     fc_to_builder, generation, schedule_update, schedule_update_any, use_hook, vdom_is_rendering,
-    AnyValue, Attribute, AttributeValue, BoxedContext, CapturedError, Component, ComponentFunction,
-    CrossPlatformConfig, DynamicNode, Element, ElementId, Event, Fragment, HasAttributes,
-    IntoDynNode, Mutation, Mutations, NoOpMutations, PlatformBuilder, Properties, RenderReturn,
-    Runtime, ScopeId, ScopeState, Task, Template, TemplateAttribute, TemplateNode, VComponent,
-    VNode, VNodeInner, VPlaceholder, VText, VirtualDom, WriteMutations,
+    AnyProps, AnyValue, Attribute, AttributeValue, BoxedContext, CapturedError, Component,
+    ComponentFunction, CrossPlatformConfig, DynamicNode, Element, ElementId, Event, Fragment,
+    HasAttributes, IntoDynNode, Mutation, Mutations, NoOpMutations, PlatformBuilder, Properties,
+    RenderReturn, Runtime, ScopeId, ScopeState, Task, Template, TemplateAttribute, TemplateNode,
+    VComponent, VNode, VNodeInner, VPlaceholder, VProps, VText, VirtualDom, WriteMutations,
 };
 
 /// The purpose of this module is to alleviate imports of many common types

+ 6 - 1
packages/core/src/nodes.rs

@@ -529,7 +529,12 @@ impl VComponent {
         P: Properties + 'static,
     {
         let render_fn = component.id();
-        let props = new_any_props(component, <P as Properties>::memoize, props, fn_name);
+        let props = Box::new(new_any_props(
+            component,
+            <P as Properties>::memoize,
+            props,
+            fn_name,
+        ));
 
         VComponent {
             name: fn_name,

+ 17 - 20
packages/core/src/platform.rs

@@ -1,8 +1,9 @@
 use std::any::Any;
 
 use crate::{
-    properties::{ComponentFunction, RootProps},
-    VComponent, VirtualDom,
+    any_props::{new_any_props, AnyProps, VProps},
+    properties::ComponentFunction,
+    VirtualDom,
 };
 
 /// A boxed object that can be injected into a component's context.
@@ -43,30 +44,26 @@ impl<T: Any + Clone> ClonableAny for T {
 }
 
 /// The platform-independent part of the config needed to launch an application.
-pub struct CrossPlatformConfig {
+pub struct CrossPlatformConfig<P: AnyProps> {
     /// The root component function.
-    component: VComponent,
+    component: P,
     /// The contexts to provide to the root component.
     root_contexts: Vec<BoxedContext>,
 }
 
-impl CrossPlatformConfig {
+impl<F: ComponentFunction<Props, M>, Props: Clone + 'static, M: 'static>
+    CrossPlatformConfig<VProps<F, Props, M>>
+{
     /// Create a new cross-platform config.
-    pub fn new<Props: Clone + 'static, M: 'static>(
-        component: impl ComponentFunction<Props, M>,
-        props: Props,
-        root_contexts: Vec<BoxedContext>,
-    ) -> Self {
-        Self {
-            component: VComponent::new(
-                move |props: RootProps<Props>| component.rebuild(props.0),
-                RootProps(props),
-                "root",
-            ),
+    pub fn new(component: F, props: Props, root_contexts: Vec<BoxedContext>) -> Self {
+        CrossPlatformConfig {
+            component: new_any_props(component, |_, _| true, props, "root"),
             root_contexts,
         }
     }
+}
 
+impl<P: AnyProps> CrossPlatformConfig<P> {
     /// Push a new context into the root component's context.
     pub fn push_context<T: Any + Clone + 'static>(&mut self, context: T) {
         self.root_contexts.push(BoxedContext::new(context));
@@ -85,18 +82,18 @@ impl CrossPlatformConfig {
 }
 
 /// A builder to launch a specific platform.
-pub trait PlatformBuilder {
+pub trait PlatformBuilder<P: AnyProps> {
     /// The platform-specific config needed to launch an application.
     type Config: Default;
 
     /// Launch the app.
-    fn launch(config: CrossPlatformConfig, platform_config: Self::Config);
+    fn launch(config: CrossPlatformConfig<P>, platform_config: Self::Config);
 }
 
-impl PlatformBuilder for () {
+impl<P: AnyProps> PlatformBuilder<P> for () {
     type Config = ();
 
-    fn launch(_: CrossPlatformConfig, _: Self::Config) {
+    fn launch(_: CrossPlatformConfig<P>, _: Self::Config) {
         panic!("No platform is currently enabled. Please enable a platform feature for the dioxus crate.");
     }
 }

+ 6 - 12
packages/core/src/virtual_dom.rs

@@ -3,6 +3,7 @@
 //! This module provides the primary mechanics to create a hook-based, concurrent VDOM for Rust.
 
 use crate::{
+    any_props::{new_any_props, AnyProps},
     arena::ElementId,
     innerlude::{
         DirtyScope, ElementRef, ErrorBoundary, NoOpMutations, SchedulerMsg, ScopeState, VNodeMount,
@@ -10,10 +11,9 @@ use crate::{
     },
     nodes::RenderReturn,
     nodes::{Template, TemplateId},
-    properties::RootProps,
     runtime::{Runtime, RuntimeGuard},
     scopes::ScopeId,
-    AttributeValue, BoxedContext, ComponentFunction, Element, Event, Mutations, Task, VComponent,
+    AttributeValue, BoxedContext, ComponentFunction, Element, Event, Mutations, Task,
 };
 use futures_util::{pin_mut, StreamExt};
 use rustc_hash::{FxHashMap, FxHashSet};
@@ -259,17 +259,11 @@ impl VirtualDom {
     /// let mut dom = VirtualDom::new_with_props(Example, SomeProps { name: "jane" });
     /// let mutations = dom.rebuild();
     /// ```
-    pub fn new_with_props<P: Clone + 'static, M>(
+    pub fn new_with_props<P: Clone + 'static, M: 'static>(
         root: impl ComponentFunction<P, M>,
         root_props: P,
     ) -> Self {
-        let vcomponent = VComponent::new(
-            move |props: RootProps<P>| root.rebuild(props.0),
-            RootProps(root_props),
-            "root",
-        );
-
-        Self::new_with_component(vcomponent)
+        Self::new_with_component(new_any_props(root, |_, _| true, root_props, "root"))
     }
 
     /// Create a new virtualdom and build it immediately
@@ -309,7 +303,7 @@ impl VirtualDom {
     /// let mut dom = VirtualDom::new_from_root(VComponent::new(Example, SomeProps { name: "jane" }, "Example"));
     /// let mutations = dom.rebuild();
     /// ```
-    pub fn new_with_component(root: VComponent) -> Self {
+    pub fn new_with_component(root: impl AnyProps + 'static) -> Self {
         let (tx, rx) = futures_channel::mpsc::unbounded();
 
         let mut dom = Self {
@@ -324,7 +318,7 @@ impl VirtualDom {
             suspended_scopes: Default::default(),
         };
 
-        let root = dom.new_scope(root.props, "app");
+        let root = dom.new_scope(Box::new(root), "app");
 
         // Unlike react, we provide a default error boundary that just renders the error as a string
         root.context()

+ 5 - 4
packages/desktop/src/app.rs

@@ -10,6 +10,7 @@ use crate::{
     webview::WebviewInstance,
 };
 use crossbeam_channel::Receiver;
+use dioxus_core::AnyProps;
 use dioxus_core::{CrossPlatformConfig, ElementId};
 use dioxus_html::{
     native_bind::NativeFileEngine, FileEngine, HasFileData, HasFormData, HtmlEvent,
@@ -28,10 +29,10 @@ use tao::{
 };
 
 /// The single top-level object that manages all the running windows, assets, shortcuts, etc
-pub(crate) struct App {
+pub(crate) struct App<P: AnyProps> {
     // move the props into a cell so we can pop it out later to create the first window
     // iOS panics if we create a window before the event loop is started, so we toss them into a cell
-    pub(crate) dioxus_config: Cell<Option<CrossPlatformConfig>>,
+    pub(crate) dioxus_config: Cell<Option<CrossPlatformConfig<P>>>,
     pub(crate) cfg: Cell<Option<Config>>,
 
     // Stuff we need mutable access to
@@ -58,10 +59,10 @@ pub struct SharedContext {
     pub(crate) target: EventLoopWindowTarget<UserWindowEvent>,
 }
 
-impl App {
+impl<P: AnyProps> App<P> {
     pub fn new(
         cfg: Config,
-        dioxus_config: CrossPlatformConfig,
+        dioxus_config: CrossPlatformConfig<P>,
     ) -> (EventLoop<UserWindowEvent>, Self) {
         let event_loop = EventLoopBuilder::<UserWindowEvent>::with_user_event().build();
 

+ 6 - 3
packages/desktop/src/launch.rs

@@ -10,7 +10,10 @@ use tao::event::{Event, StartCause, WindowEvent};
 ///
 /// This will block the main thread, and *must* be spawned on the main thread. This function does not assume any runtime
 /// and is equivalent to calling launch_with_props with the tokio feature disabled.
-pub fn launch_with_props_blocking(dioxus_cfg: CrossPlatformConfig, desktop_config: Config) {
+pub fn launch_with_props_blocking<P: AnyProps>(
+    dioxus_cfg: CrossPlatformConfig<P>,
+    desktop_config: Config,
+) {
     let (event_loop, mut app) = App::new(desktop_config, dioxus_cfg);
 
     event_loop.run(move |window_event, _, control_flow| {
@@ -50,10 +53,10 @@ pub fn launch_with_props_blocking(dioxus_cfg: CrossPlatformConfig, desktop_confi
 /// The desktop renderer platform
 pub struct DesktopPlatform;
 
-impl PlatformBuilder for DesktopPlatform {
+impl<P: AnyProps> PlatformBuilder<P> for DesktopPlatform {
     type Config = Config;
 
-    fn launch(config: dioxus_core::CrossPlatformConfig, platform_config: Self::Config) {
+    fn launch(config: dioxus_core::CrossPlatformConfig<P>, platform_config: Self::Config) {
         #[cfg(feature = "tokio")]
         tokio::runtime::Builder::new_multi_thread()
             .enable_all()

+ 17 - 16
packages/dioxus/src/launch.rs

@@ -3,21 +3,22 @@
 use std::any::Any;
 
 use crate::prelude::*;
-use dioxus_core::prelude::*;
+use dioxus_core::VProps;
+use dioxus_core::{prelude::*, AnyProps};
 use dioxus_core::{BoxedContext, CrossPlatformConfig, PlatformBuilder};
 
 /// A builder for a fullstack app.
-pub struct LaunchBuilder<Platform: PlatformBuilder = CurrentPlatform> {
-    cross_platform_config: CrossPlatformConfig,
-    platform_config: Option<<Platform as PlatformBuilder>::Config>,
+pub struct LaunchBuilder<P: AnyProps, Platform: PlatformBuilder<P> = CurrentPlatform> {
+    cross_platform_config: CrossPlatformConfig<P>,
+    platform_config: Option<<Platform as PlatformBuilder<P>>::Config>,
 }
 
 // Default platform builder
-impl LaunchBuilder {
+impl<F: ComponentFunction<Props, M>, Props: Clone + Default + 'static, M: 'static>
+    LaunchBuilder<VProps<F, Props, M>>
+{
     /// Create a new builder for your application. This will create a launch configuration for the current platform based on the features enabled on the `dioxus` crate.
-    pub fn new<Props: Clone + Default + 'static, M: 'static>(
-        component: impl ComponentFunction<Props, M>,
-    ) -> Self {
+    pub fn new(component: F) -> Self {
         Self {
             cross_platform_config: CrossPlatformConfig::new(
                 component,
@@ -29,10 +30,7 @@ impl LaunchBuilder {
     }
 
     /// Create a new builder for your application with some root props. This will create a launch configuration for the current platform based on the features enabled on the `dioxus` crate.
-    pub fn new_with_props<Props: Clone + 'static, M: 'static>(
-        component: impl ComponentFunction<Props, M>,
-        props: Props,
-    ) -> Self {
+    pub fn new_with_props(component: F, props: Props) -> Self {
         Self {
             cross_platform_config: CrossPlatformConfig::new(component, props, Default::default()),
             platform_config: None,
@@ -40,7 +38,7 @@ impl LaunchBuilder {
     }
 }
 
-impl<Platform: PlatformBuilder> LaunchBuilder<Platform> {
+impl<P: AnyProps, Platform: PlatformBuilder<P>> LaunchBuilder<P, Platform> {
     /// Inject state into the root component's context.
     pub fn context(mut self, state: impl Any + Clone + 'static) -> Self {
         self.cross_platform_config
@@ -49,7 +47,10 @@ impl<Platform: PlatformBuilder> LaunchBuilder<Platform> {
     }
 
     /// Provide a platform-specific config to the builder.
-    pub fn cfg(mut self, config: impl Into<Option<<Platform as PlatformBuilder>::Config>>) -> Self {
+    pub fn cfg(
+        mut self,
+        config: impl Into<Option<<Platform as PlatformBuilder<P>>::Config>>,
+    ) -> Self {
         if let Some(config) = config.into() {
             self.platform_config = Some(config);
         }
@@ -67,7 +68,7 @@ impl<Platform: PlatformBuilder> LaunchBuilder<Platform> {
 }
 
 #[cfg(feature = "web")]
-impl LaunchBuilder<dioxus_web::WebPlatform> {
+impl<P: AnyProps> LaunchBuilder<P, dioxus_web::WebPlatform> {
     /// Launch your web application.
     pub fn launch_web(self) {
         dioxus_web::WebPlatform::launch(
@@ -78,7 +79,7 @@ impl LaunchBuilder<dioxus_web::WebPlatform> {
 }
 
 #[cfg(feature = "desktop")]
-impl LaunchBuilder<dioxus_desktop::DesktopPlatform> {
+impl<P: AnyProps> LaunchBuilder<P, dioxus_desktop::DesktopPlatform> {
     /// Launch your desktop application.
     pub fn launch_desktop(self) {
         dioxus_desktop::DesktopPlatform::launch(

+ 4 - 2
packages/fullstack/src/launch.rs

@@ -1,12 +1,14 @@
 //! Launch helper macros for fullstack apps
 #![allow(unused)]
 use crate::prelude::*;
-use dioxus_lib::prelude::*;
+use dioxus_lib::prelude::{dioxus_core::AnyProps, *};
 
 /// The desktop renderer platform
 pub struct FullstackPlatform;
 
-impl<Props: Clone + 'static> dioxus_core::PlatformBuilder<Props> for FullstackPlatform {
+impl<Props: AnyProps + Send + Sync + 'static> dioxus_core::PlatformBuilder<Props>
+    for FullstackPlatform
+{
     type Config = Config;
 
     fn launch(config: dioxus_core::CrossPlatformConfig<Props>, platform_config: Self::Config) {}