1
0
Эх сурвалжийг харах

Remove phantom markers and just default to Rc<dyn Fn(props) -> Element> where it makes sense

Jonathan Kelley 1 жил өмнө
parent
commit
c94af9538b

+ 8 - 13
packages/core/src/any_props.rs

@@ -1,5 +1,5 @@
-use crate::{nodes::RenderReturn, properties::ComponentFunction};
-use std::{any::Any, marker::PhantomData, ops::Deref, panic::AssertUnwindSafe};
+use crate::{nodes::RenderReturn, Component};
+use std::{any::Any, ops::Deref, panic::AssertUnwindSafe};
 
 /// A boxed version of AnyProps that can be cloned
 pub(crate) struct BoxedAnyProps {
@@ -38,17 +38,16 @@ pub(crate) trait AnyProps {
     fn duplicate(&self) -> Box<dyn AnyProps>;
 }
 
-pub(crate) struct VProps<P: 'static, F: ComponentFunction<Phantom, Props = P>, Phantom: 'static> {
-    pub render_fn: F,
+pub(crate) struct VProps<P: 'static> {
+    pub render_fn: Component<P>,
     pub memo: fn(&P, &P) -> bool,
     pub props: P,
     pub name: &'static str,
-    phantom: PhantomData<Phantom>,
 }
 
-impl<P: 'static, F: ComponentFunction<Phantom, Props = P>, Phantom: 'static> VProps<P, F, Phantom> {
+impl<P: 'static> VProps<P> {
     pub(crate) fn new(
-        render_fn: F,
+        render_fn: Component<P>,
         memo: fn(&P, &P) -> bool,
         props: P,
         name: &'static str,
@@ -58,14 +57,11 @@ impl<P: 'static, F: ComponentFunction<Phantom, Props = P>, Phantom: 'static> VPr
             memo,
             props,
             name,
-            phantom: PhantomData,
         }
     }
 }
 
-impl<P: Clone + 'static, F: ComponentFunction<Phantom, Props = P>, Phantom> AnyProps
-    for VProps<P, F, Phantom>
-{
+impl<P: Clone + 'static> AnyProps for VProps<P> {
     fn memoize(&self, other: &dyn Any) -> bool {
         match other.downcast_ref::<P>() {
             Some(other) => (self.memo)(&self.props, other),
@@ -80,7 +76,7 @@ impl<P: Clone + 'static, F: ComponentFunction<Phantom, Props = P>, Phantom> AnyP
     fn render(&self) -> RenderReturn {
         let res = std::panic::catch_unwind(AssertUnwindSafe(move || {
             // Call the render function directly
-            self.render_fn.call(self.props.clone())
+            (self.render_fn)(self.props.clone())
         }));
 
         match res {
@@ -100,7 +96,6 @@ impl<P: Clone + 'static, F: ComponentFunction<Phantom, Props = P>, Phantom> AnyP
             memo: self.memo,
             props: self.props.clone(),
             name: self.name,
-            phantom: PhantomData,
         })
     }
 }

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

@@ -70,12 +70,12 @@ pub(crate) mod innerlude {
     ///     Example {}
     /// )
     /// ```
-    pub type Component<P = ()> = fn(P) -> Element;
+    pub type Component<P = ()> = std::rc::Rc<dyn Fn(P) -> Element>;
 }
 
 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,
+    AnyValue, Attribute, AttributeValue, BoxedContext, CapturedError, Component,
     CrossPlatformConfig, DynamicNode, Element, ElementId, Event, Fragment, HasAttributes,
     IntoDynNode, Mutation, Mutations, NoOpMutations, PlatformBuilder, Properties, RenderReturn,
     ScopeId, ScopeState, Task, Template, TemplateAttribute, TemplateNode, VComponent, VNode,
@@ -90,9 +90,9 @@ pub mod prelude {
         consume_context, consume_context_from_scope, current_scope_id, fc_to_builder, generation,
         has_context, needs_update, parent_scope, provide_context, provide_root_context,
         remove_future, schedule_update, schedule_update_any, spawn, spawn_forever, suspend,
-        try_consume_context, use_error_boundary, use_hook, AnyValue, Attribute, Component, Element,
-        ErrorBoundary, Event, EventHandler, Fragment, HasAttributes, IntoAttributeValue,
-        IntoDynNode, Properties, Runtime, RuntimeGuard, ScopeId, ScopeState, Task, Template,
-        TemplateAttribute, TemplateNode, Throw, VNode, VNodeInner, VirtualDom,
+        try_consume_context, use_error_boundary, use_hook, AnyValue, Attribute, Component,
+        ComponentFn, Element, ErrorBoundary, Event, EventHandler, Fragment, HasAttributes,
+        IntoAttributeValue, IntoDynNode, Properties, Runtime, RuntimeGuard, ScopeId, ScopeState,
+        Task, Template, TemplateAttribute, TemplateNode, Throw, VNode, VNodeInner, VirtualDom,
     };
 }

+ 12 - 13
packages/core/src/nodes.rs

@@ -1,13 +1,14 @@
-use crate::innerlude::{ElementRef, EventHandler, MountId};
-use crate::properties::ComponentFunction;
 use crate::{
     any_props::{BoxedAnyProps, VProps},
     innerlude::ScopeState,
 };
 use crate::{arena::ElementId, Element, Event};
+use crate::{
+    innerlude::{ElementRef, EventHandler, MountId},
+    properties::ComponentFn,
+};
 use crate::{Properties, VirtualDom};
 use core::panic;
-use std::ops::Deref;
 use std::rc::Rc;
 use std::vec;
 use std::{
@@ -15,6 +16,7 @@ use std::{
     cell::Cell,
     fmt::{Arguments, Debug},
 };
+use std::{ffi::c_void, ops::Deref};
 
 pub type TemplateId = &'static str;
 
@@ -511,7 +513,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: TypeId,
+    pub(crate) render_fn: *const c_void,
 
     pub(crate) props: BoxedAnyProps,
 }
@@ -531,22 +533,19 @@ impl VComponent {
     /// fn(Props) -> Element;
     /// async fn(Scope<Props<'_>>) -> Element;
     /// ```
-    pub fn new<F: ComponentFunction<P> + 'static, P: 'static>(
-        component: F,
-        props: F::Props,
-        fn_name: &'static str,
-    ) -> Self
+    pub fn new<P, M>(component: impl ComponentFn<P, M>, props: P, fn_name: &'static str) -> Self
     where
         // The properties must be valid until the next bump frame
-        F::Props: Properties + 'static,
+        P: Properties + 'static,
     {
-        let render_fn_id = TypeId::of::<F>();
-        let vcomp = VProps::new(component, F::Props::memoize, props, fn_name);
+        let component = Rc::new(component).as_component();
+        let render_fn = component.as_ref() as *const _ as *const c_void;
+        let vcomp = VProps::new(component, <P as Properties>::memoize, props, fn_name);
 
         VComponent {
             name: fn_name,
-            render_fn: render_fn_id,
             props: BoxedAnyProps::new(vcomp),
+            render_fn,
         }
     }
 

+ 13 - 26
packages/core/src/platform.rs

@@ -1,6 +1,6 @@
-use std::{any::Any, marker::PhantomData};
+use std::any::Any;
 
-use crate::{ComponentFunction, VirtualDom};
+use crate::{properties::ComponentFn, Component, VirtualDom};
 
 /// A boxed object that can be injected into a component's context.
 pub struct BoxedContext(Box<dyn ClonableAny>);
@@ -40,33 +40,26 @@ impl<T: Any + Clone> ClonableAny for T {
 }
 
 /// The platform-independent part of the config needed to launch an application.
-pub struct CrossPlatformConfig<
-    Component: ComponentFunction<Phantom, Props = Props>,
-    Props: Clone + 'static,
-    Phantom: 'static,
-> {
+pub struct CrossPlatformConfig<Props: Clone + 'static> {
     /// The root component function.
-    pub component: Component,
+    pub component: Component<Props>,
     /// The props for the root component.
     pub props: Props,
     /// The contexts to provide to the root component.
     pub root_contexts: Vec<BoxedContext>,
-    _phantom: PhantomData<Phantom>,
 }
 
-impl<
-        Component: ComponentFunction<Phantom, Props = Props>,
-        Props: Clone + 'static,
-        Phantom: 'static,
-    > CrossPlatformConfig<Component, Props, Phantom>
-{
+impl<Props: Clone + 'static> CrossPlatformConfig<Props> {
     /// Create a new cross-platform config.
-    pub fn new(component: Component, props: Props, root_contexts: Vec<BoxedContext>) -> Self {
+    pub fn new<M>(
+        component: impl ComponentFn<Props, M>,
+        props: Props,
+        root_contexts: Vec<BoxedContext>,
+    ) -> Self {
         Self {
-            component,
+            component: ComponentFn::as_component(std::rc::Rc::new(component)),
             props,
             root_contexts,
-            _phantom: PhantomData,
         }
     }
 
@@ -88,19 +81,13 @@ pub trait PlatformBuilder<Props: Clone + 'static> {
     type Config: Default;
 
     /// Launch the app.
-    fn launch<Component: ComponentFunction<Phantom, Props = Props>, Phantom: 'static>(
-        config: CrossPlatformConfig<Component, Props, Phantom>,
-        platform_config: Self::Config,
-    );
+    fn launch(config: CrossPlatformConfig<Props>, platform_config: Self::Config);
 }
 
 impl<Props: Clone + 'static> PlatformBuilder<Props> for () {
     type Config = ();
 
-    fn launch<Component: ComponentFunction<Phantom, Props = Props>, Phantom: 'static>(
-        _: CrossPlatformConfig<Component, Props, Phantom>,
-        _: Self::Config,
-    ) {
+    fn launch(_: CrossPlatformConfig<Props>, _: Self::Config) {
         panic!("No platform is currently enabled. Please enable a platform feature for the dioxus crate.");
     }
 }

+ 34 - 83
packages/core/src/properties.rs

@@ -1,3 +1,5 @@
+use std::rc::Rc;
+
 use crate::innerlude::*;
 
 /// Every "Props" used for a component must implement the `Properties` trait. This trait gives some hints to Dioxus
@@ -29,11 +31,7 @@ pub trait Properties: Clone + Sized + 'static {
     /// Create a builder for this component.
     fn builder() -> Self::Builder;
 
-    /// Memoization can only happen if the props are valid for the 'static lifetime
-    ///
-    /// # Safety
-    /// The user must know if their props are static, but if they make a mistake, UB happens
-    /// Therefore it's unsafe to memoize.
+    /// Compare two props to see if they are memoizable.
     fn memoize(&self, other: &Self) -> bool;
 }
 
@@ -56,98 +54,51 @@ 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<F: ComponentFunction<P>, P>(
-    _: F,
-) -> <<F as ComponentFunction<P>>::Props as Properties>::Builder
+pub fn fc_to_builder<P, M>(_: impl ComponentFn<P, M>) -> <P as Properties>::Builder
 where
-    F::Props: Properties,
+    P: Properties,
 {
-    F::Props::builder()
+    P::builder()
 }
 
-/// Every component used in rsx must implement the `ComponentFunction` trait. This trait tells dioxus how your component should be rendered.
-///
-/// Dioxus automatically implements this trait for any function that either takes no arguments or a single props argument and returns an Element.
-///
-/// ## Example
-///
-/// For components that take no props:
-///
-/// ```rust
-/// fn app() -> Element {
-///     rsx! {
-///         div {}
-///     }
-/// }
-/// ```
-///
-/// For props that take a props struct:
-///
-/// ```rust
-/// #[derive(Props, PartialEq, Clone)]
-/// struct MyProps {
-///    data: String
-/// }
-///
-/// fn app(props: MyProps) -> Element {
-///     rsx! {
-///         div {
-///             "{props.data}"
-///         }
-///     }
-/// }
-/// ```
-///
-/// Or you can use the #[component] macro to automatically implement create the props struct:
-///
-/// ```rust
-/// #[component]
-/// fn app(data: String) -> Element {
-///     rsx! {
-///         div {
-///             "{data}"
-///         }
-///     }
-/// }
-/// ```
-///
-/// > Note: If you get an error about the `ComponentFunction` trait not being implemented: make sure your props implements the `Properties` trait or if you would like to declare your props inline, make sure you use the #[component] macro on your function.
-pub trait ComponentFunction<P>: Clone + 'static {
-    /// The props type for this component.
-    type Props: 'static;
-
-    /// Run the component function with the given props.
-    fn call(&self, props: Self::Props) -> Element;
+/// Any component that implements the `ComponentFn` trait can be used as a component.
+pub trait ComponentFn<Props, Marker> {
+    /// Convert the component to a function that takes props and returns an element.
+    fn as_component(self: Rc<Self>) -> Component<Props>;
 }
 
-impl<T: 'static, F: Fn(T) -> Element + Clone + 'static> ComponentFunction<(T,)> for F {
-    type Props = T;
-
-    fn call(&self, props: T) -> Element {
-        self(props)
+/// Accept pre-formed component render functions as components
+impl<P> ComponentFn<P, ()> for Component<P> {
+    fn as_component(self: Rc<Self>) -> Component<P> {
+        self.as_ref().clone()
     }
 }
 
-#[doc(hidden)]
-pub struct ZeroElementMarker;
-impl<F: Fn() -> Element + Clone + 'static> ComponentFunction<ZeroElementMarker> for F {
-    type Props = ();
+/// Accept any callbacks that take props
+impl<F: Fn(P) -> Element + 'static, P> ComponentFn<P, ()> for F {
+    fn as_component(self: Rc<Self>) -> Component<P> {
+        self
+    }
+}
 
-    fn call(&self, _: ()) -> Element {
-        self()
+/// Accept any callbacks that take no props
+pub struct EmptyMarker;
+impl<F: Fn() -> Element + 'static> ComponentFn<(), EmptyMarker> for F {
+    fn as_component(self: Rc<Self>) -> Rc<dyn Fn(()) -> Element> {
+        Rc::new(move |_| self())
     }
 }
 
 #[test]
-fn test_empty_builder() {
-    fn app() -> Element {
-        unimplemented!()
+fn it_works_maybe() {
+    fn test(_: ()) -> Element {
+        todo!()
     }
-    fn app2(_: ()) -> Element {
-        unimplemented!()
+    fn test2() -> Element {
+        todo!()
     }
-    let builder = fc_to_builder(app);
-    builder.build();
-    let builder = fc_to_builder(app2);
-    builder.build();
+
+    let callable: Rc<dyn ComponentFn<(), ()>> = Rc::new(test) as Rc<dyn ComponentFn<_, _>>;
+    let callable2: Rc<dyn ComponentFn<(), EmptyMarker>> =
+        Rc::new(test2) as Rc<dyn ComponentFn<_, _>>;
 }

+ 10 - 8
packages/core/src/virtual_dom.rs

@@ -11,6 +11,7 @@ use crate::{
     },
     nodes::RenderReturn,
     nodes::{Template, TemplateId},
+    properties::ComponentFn,
     runtime::{Runtime, RuntimeGuard},
     scopes::ScopeId,
     AttributeValue, BoxedContext, Element, Event, Mutations,
@@ -227,7 +228,7 @@ impl VirtualDom {
     ///
     /// Note: the VirtualDom is not progressed, you must either "run_with_deadline" or use "rebuild" to progress it.
     pub fn new(app: fn() -> Element) -> Self {
-        Self::new_with_props(app, ())
+        Self::new_with_props(move || app(), ())
     }
 
     /// Create a new virtualdom and build it immediately
@@ -267,12 +268,8 @@ impl VirtualDom {
     /// let mut dom = VirtualDom::new_with_props(Example, SomeProps { name: "jane" });
     /// let mutations = dom.rebuild();
     /// ```
-    pub fn new_with_props<
-        F: crate::ComponentFunction<Phantom, Props = P>,
-        P: Clone + 'static,
-        Phantom: 'static,
-    >(
-        root: F,
+    pub fn new_with_props<P: Clone + 'static, M>(
+        root: impl ComponentFn<P, M>,
         root_props: P,
     ) -> Self {
         let (tx, rx) = futures_channel::mpsc::unbounded();
@@ -290,7 +287,12 @@ impl VirtualDom {
         };
 
         let root = dom.new_scope(
-            BoxedAnyProps::new(VProps::new(root, |_, _| true, root_props, "root")),
+            BoxedAnyProps::new(VProps::new(
+                Rc::new(root).as_component(),
+                |_, _| true,
+                root_props,
+                "root",
+            )),
             "app",
         );
 

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

@@ -10,7 +10,7 @@ use crate::{
     webview::WebviewInstance,
 };
 use crossbeam_channel::Receiver;
-use dioxus_core::{ComponentFunction, CrossPlatformConfig, ElementId};
+use dioxus_core::{CrossPlatformConfig, ElementId};
 use dioxus_html::{
     native_bind::NativeFileEngine, FileEngine, HasFileData, HasFormData, HtmlEvent,
     PlatformEventData,
@@ -29,14 +29,10 @@ use tao::{
 };
 
 /// The single top-level object that manages all the running windows, assets, shortcuts, etc
-pub(crate) struct App<
-    Component: ComponentFunction<Phantom, Props = Props>,
-    Props: Clone + 'static,
-    Phantom: 'static,
-> {
+pub(crate) struct App<Props: Clone + 'static> {
     // 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<Component, Props, Phantom>>>,
+    pub(crate) dioxus_config: Cell<Option<CrossPlatformConfig<Props>>>,
     pub(crate) cfg: Cell<Option<Config>>,
 
     // Stuff we need mutable access to
@@ -49,8 +45,6 @@ pub(crate) struct App<
     ///
     /// This includes stuff like the event handlers, shortcuts, etc as well as ways to modify *other* windows
     pub(crate) shared: Rc<SharedContext>,
-
-    phantom: PhantomData<Phantom>,
 }
 
 /// A bundle of state shared between all the windows, providing a way for us to communicate with running webview.
@@ -65,15 +59,10 @@ pub struct SharedContext {
     pub(crate) target: EventLoopWindowTarget<UserWindowEvent>,
 }
 
-impl<
-        Component: ComponentFunction<Phantom, Props = Props>,
-        Props: Clone + 'static,
-        Phantom: 'static,
-    > App<Component, Props, Phantom>
-{
+impl<Props: Clone + 'static> App<Props> {
     pub fn new(
         cfg: Config,
-        dioxus_config: CrossPlatformConfig<Component, Props, Phantom>,
+        dioxus_config: CrossPlatformConfig<Props>,
     ) -> (EventLoop<UserWindowEvent>, Self) {
         let event_loop = EventLoopBuilder::<UserWindowEvent>::with_user_event().build();
 
@@ -92,7 +81,6 @@ impl<
                 proxy: event_loop.create_proxy(),
                 target: event_loop.clone(),
             }),
-            phantom: PhantomData,
         };
 
         // Set the event converter

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

@@ -10,12 +10,8 @@ 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<
-    Component: ComponentFunction<Phantom, Props = Props>,
-    Props: Clone + 'static,
-    Phantom: 'static,
->(
-    dioxus_cfg: CrossPlatformConfig<Component, Props, Phantom>,
+pub fn launch_with_props_blocking<Props: Clone + 'static>(
+    dioxus_cfg: CrossPlatformConfig<Props>,
     desktop_config: Config,
 ) {
     let (event_loop, mut app) = App::new(desktop_config, dioxus_cfg);
@@ -60,10 +56,7 @@ pub struct DesktopPlatform;
 impl<Props: Clone + 'static> PlatformBuilder<Props> for DesktopPlatform {
     type Config = Config;
 
-    fn launch<Component: ComponentFunction<Phantom, Props = Props>, Phantom: 'static>(
-        config: dioxus_core::CrossPlatformConfig<Component, Props, Phantom>,
-        platform_config: Self::Config,
-    ) {
+    fn launch(config: dioxus_core::CrossPlatformConfig<Props>, platform_config: Self::Config) {
         #[cfg(feature = "tokio")]
         tokio::runtime::Builder::new_multi_thread()
             .enable_all()

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

@@ -4,29 +4,19 @@ use std::any::Any;
 
 use crate::prelude::*;
 use dioxus_core::prelude::*;
-use dioxus_core::ComponentFunction;
 use dioxus_core::{BoxedContext, CrossPlatformConfig, PlatformBuilder};
 
 /// A builder for a fullstack app.
-pub struct LaunchBuilder<
-    Component: ComponentFunction<Phantom, Props = Props>,
-    Props: Clone + 'static,
-    Phantom: 'static,
-    Platform: PlatformBuilder<Props> = CurrentPlatform,
-> {
-    cross_platform_config: CrossPlatformConfig<Component, Props, Phantom>,
+pub struct LaunchBuilder<Props: Clone + 'static, Platform: PlatformBuilder<Props> = CurrentPlatform>
+{
+    cross_platform_config: CrossPlatformConfig<Props>,
     platform_config: Option<<Platform as PlatformBuilder<Props>>::Config>,
 }
 
 // Default platform builder
-impl<
-        Component: ComponentFunction<Phantom, Props = Props>,
-        Props: Clone + 'static,
-        Phantom: 'static,
-    > LaunchBuilder<Component, Props, Phantom>
-{
+impl<Props: Clone + 'static> LaunchBuilder<Props> {
     /// 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(component: Component) -> Self
+    pub fn new<M>(component: impl ComponentFn<Props, M>) -> Self
     where
         Props: Default,
     {
@@ -41,13 +31,7 @@ impl<
     }
 }
 
-impl<
-        Component: ComponentFunction<Phantom, Props = Props>,
-        Props: Clone + 'static,
-        Phantom: 'static,
-        Platform: PlatformBuilder<Props>,
-    > LaunchBuilder<Component, Props, Phantom, Platform>
-{
+impl<Props: Clone + 'static, Platform: PlatformBuilder<Props>> LaunchBuilder<Props, Platform> {
     /// Pass some props to your application.
     pub fn props(mut self, props: Props) -> Self {
         self.cross_platform_config.props = props;
@@ -84,12 +68,7 @@ impl<
 }
 
 #[cfg(feature = "web")]
-impl<
-        Component: ComponentFunction<Phantom, Props = Props>,
-        Props: Clone + 'static,
-        Phantom: 'static,
-    > LaunchBuilder<Component, Props, Phantom, dioxus_web::WebPlatform>
-{
+impl<Props: Clone + 'static> LaunchBuilder<Props, dioxus_web::WebPlatform> {
     /// Launch your web application.
     pub fn launch_web(self) {
         dioxus_web::WebPlatform::launch(
@@ -100,12 +79,7 @@ impl<
 }
 
 #[cfg(feature = "desktop")]
-impl<
-        Component: ComponentFunction<Phantom, Props = Props>,
-        Props: Clone + 'static,
-        Phantom: 'static,
-    > LaunchBuilder<Component, Props, Phantom, dioxus_desktop::DesktopPlatform>
-{
+impl<Props: Clone + 'static> LaunchBuilder<Props, dioxus_desktop::DesktopPlatform> {
     /// Launch your desktop application.
     pub fn launch_desktop(self) {
         dioxus_desktop::DesktopPlatform::launch(
@@ -123,42 +97,27 @@ type CurrentPlatform = dioxus_web::WebPlatform;
 type CurrentPlatform = ();
 
 /// Launch your application without any additional configuration. See [`LaunchBuilder`] for more options.
-pub fn launch<
-    Component: ComponentFunction<Phantom, Props = Props>,
-    Props: Clone + 'static,
-    Phantom: 'static,
->(
-    component: Component,
-) where
-    Props: Default,
+pub fn launch<Props, Marker>(component: impl ComponentFn<Props, Marker>)
+where
+    Props: Default + Clone + 'static,
 {
     LaunchBuilder::new(component).launch()
 }
 
 #[cfg(feature = "web")]
 /// Launch your web application without any additional configuration. See [`LaunchBuilder`] for more options.
-pub fn launch_web<
-    Component: ComponentFunction<Phantom, Props = Props>,
-    Props: Clone + 'static,
-    Phantom: 'static,
->(
-    component: Component,
-) where
-    Props: Default,
+pub fn launch_web<Props, Marker>(component: impl ComponentFn<Props, Marker>)
+where
+    Props: Default + Clone + 'static,
 {
     LaunchBuilder::new(component).launch_web()
 }
 
 #[cfg(feature = "desktop")]
 /// Launch your desktop application without any additional configuration. See [`LaunchBuilder`] for more options.
-pub fn launch_desktop<
-    Component: ComponentFunction<Phantom, Props = Props>,
-    Props: Clone + 'static,
-    Phantom: 'static,
->(
-    component: Component,
-) where
-    Props: Default,
+pub fn launch_desktop<Props, Marker>(component: impl ComponentFn<Props, Marker>)
+where
+    Props: Default + Clone + 'static,
 {
     LaunchBuilder::new(component).launch_desktop()
 }