diff --git a/examples/router.rs b/examples/router.rs index 11465c38f..ed7fbefa4 100644 --- a/examples/router.rs +++ b/examples/router.rs @@ -43,7 +43,6 @@ fn App(cx: Scope) -> Element { } } -#[inline_props] fn NavBar(cx: Scope) -> Element { render! { nav { @@ -56,14 +55,12 @@ fn NavBar(cx: Scope) -> Element { } } -#[inline_props] fn Home(cx: Scope) -> Element { render! { h1 { "Welcome to the Dioxus Blog!" } } } -#[inline_props] fn Blog(cx: Scope) -> Element { render! { h1 { "Blog" } @@ -71,7 +68,6 @@ fn Blog(cx: Scope) -> Element { } } -#[inline_props] fn BlogList(cx: Scope) -> Element { render! { h2 { "Choose a post" } diff --git a/packages/router-macro/src/layout.rs b/packages/router-macro/src/layout.rs index 732f48201..43561797a 100644 --- a/packages/router-macro/src/layout.rs +++ b/packages/router-macro/src/layout.rs @@ -1,5 +1,5 @@ use proc_macro2::TokenStream; -use quote::{format_ident, quote}; +use quote::quote; use syn::Path; use crate::nest::{Nest, NestId}; @@ -10,25 +10,20 @@ pub struct LayoutId(pub usize); #[derive(Debug)] pub struct Layout { pub comp: Path, - pub props_name: Path, pub active_nests: Vec, } impl Layout { pub fn routable_match(&self, nests: &[Nest]) -> TokenStream { - let props_name = &self.props_name; let comp_name = &self.comp; - let name_str = self.comp.segments.last().unwrap().ident.to_string(); let dynamic_segments = self .active_nests .iter() .flat_map(|id| nests[id.0].dynamic_segments()); quote! { - let comp = #props_name { #(#dynamic_segments,)* }; - let dynamic = cx.component(#comp_name, comp, #name_str); render! { - dynamic + #comp_name { #(#dynamic_segments: #dynamic_segments,)* } } } } @@ -40,23 +35,6 @@ impl Layout { let _ = input.parse::(); let comp: Path = input.parse()?; - // Then parse the props name - let _ = input.parse::(); - let props_name = input.parse::().unwrap_or_else(|_| { - let last = format_ident!("{}Props", comp.segments.last().unwrap().ident.to_string()); - let mut segments = comp.segments.clone(); - segments.pop(); - segments.push(last.into()); - Path { - leading_colon: None, - segments, - } - }); - - Ok(Self { - comp, - props_name, - active_nests, - }) + Ok(Self { comp, active_nests }) } } diff --git a/packages/router-macro/src/lib.rs b/packages/router-macro/src/lib.rs index 771c9640f..3b5ec85d7 100644 --- a/packages/router-macro/src/lib.rs +++ b/packages/router-macro/src/lib.rs @@ -43,8 +43,8 @@ mod segment; /// #[rustfmt::skip] /// #[derive(Clone, Debug, PartialEq, Routable)] /// enum Route { -/// // Define routes with the route macro. If the name of the component is not the same as the variant, you can specify it as the second parameter and the props type as the third -/// #[route("/", IndexComponent, ComponentProps)] +/// // Define routes with the route macro. If the name of the component is not the same as the variant, you can specify it as the second parameter +/// #[route("/", IndexComponent)] /// Index {}, /// // Nests with parameters have types taken from child routes /// // Everything inside the nest has the added parameter `user_id: usize` @@ -52,7 +52,7 @@ mod segment; /// // All children of layouts will be rendered inside the Outlet in the layout component /// // Creates a Layout UserFrame that has the parameter `user_id: usize` /// #[layout(UserFrame)] -/// // If there is a component with the name Route1 and props with the name Route1Props, you do not need to pass in the component and type +/// // If there is a component with the name Route1, you do not need to pass in the component name /// #[route("/:dynamic?:query")] /// Route1 { /// // The type is taken from the first instance of the dynamic parameter @@ -78,12 +78,11 @@ mod segment; /// } /// ``` /// -/// # `#[route("path", component, props)]` +/// # `#[route("path", component)]` /// /// The `#[route]` attribute is used to define a route. It takes up to 3 parameters: /// - `path`: The path to the enum variant (relative to the parent nest) /// - (optional) `component`: The component to render when the route is matched. If not specified, the name of the variant is used -/// - (optional) `props`: The props type for the component. If not specified, the name of the variant with `Props` appended is used /// /// Routes are the most basic attribute. They allow you to define a route and the component to render when the route is matched. The component must take all dynamic parameters of the route and all parent nests. /// The next variant will be tied to the component. If you link to that variant, the component will be rendered. @@ -91,9 +90,9 @@ mod segment; /// ```rust, skip /// #[derive(Clone, Debug, PartialEq, Routable)] /// enum Route { -/// // Define routes that renders the IndexComponent that takes the IndexProps +/// // Define routes that renders the IndexComponent /// // The Index component will be rendered when the route is matched (e.g. when the user navigates to /) -/// #[route("/", Index, IndexProps)] +/// #[route("/", Index)] /// Index {}, /// } /// ``` @@ -109,7 +108,7 @@ mod segment; /// enum Route { /// // Redirects the /:id route to the Index route /// #[redirect("/:id", |_: usize| Route::Index {})] -/// #[route("/", Index, IndexProps)] +/// #[route("/", Index)] /// Index {}, /// } /// ``` @@ -131,7 +130,7 @@ mod segment; /// // This is at /blog/:id /// #[redirect("/:id", |_: usize| Route::Index {})] /// // This is at /blog -/// #[route("/", Index, IndexProps)] +/// #[route("/", Index)] /// Index {}, /// } /// ``` @@ -147,7 +146,7 @@ mod segment; /// // This is at /blog/:id /// #[redirect("/:id", |_: usize| Route::Index {})] /// // This is at /blog -/// #[route("/", Index, IndexProps)] +/// #[route("/", Index)] /// Index {}, /// // Ends the nest /// #[end_nest] @@ -161,7 +160,6 @@ mod segment; /// /// The `#[layout]` attribute is used to define a layout. It takes 2 parameters: /// - `component`: The component to render when the route is matched. If not specified, the name of the variant is used -/// - (optional) `props`: The props type for the component. If not specified, the name of the variant with `Props` appended is used /// /// The layout component allows you to wrap all children of the layout in a component. The child routes are rendered in the Outlet of the layout component. The layout component must take all dynamic parameters of the nests it is nested in. /// @@ -171,7 +169,7 @@ mod segment; /// #[layout(BlogFrame)] /// #[redirect("/:id", |_: usize| Route::Index {})] /// // Index will be rendered in the Outlet of the BlogFrame component -/// #[route("/", Index, IndexProps)] +/// #[route("/", Index)] /// Index {}, /// } /// ``` @@ -186,7 +184,7 @@ mod segment; /// #[layout(BlogFrame)] /// #[redirect("/:id", |_: usize| Route::Index {})] /// // Index will be rendered in the Outlet of the BlogFrame component -/// #[route("/", Index, IndexProps)] +/// #[route("/", Index)] /// Index {}, /// // Ends the layout /// #[end_layout] diff --git a/packages/router-macro/src/route.rs b/packages/router-macro/src/route.rs index af6d7136f..b070c2013 100644 --- a/packages/router-macro/src/route.rs +++ b/packages/router-macro/src/route.rs @@ -21,7 +21,6 @@ use crate::segment::RouteSegment; struct RouteArgs { route: LitStr, comp_name: Option, - props_name: Option, } impl Parse for RouteArgs { @@ -34,10 +33,6 @@ impl Parse for RouteArgs { let _ = input.parse::(); input.parse().ok() }, - props_name: { - let _ = input.parse::(); - input.parse().ok() - }, }) } } @@ -83,22 +78,8 @@ impl Route { Some(attr) => { let args = attr.parse_args::()?; let comp_name = args.comp_name.unwrap_or_else(|| parse_quote!(#route_name)); - let props_name = args.props_name.unwrap_or_else(|| { - let last = format_ident!( - "{}Props", - comp_name.segments.last().unwrap().ident.to_string() - ); - let mut segments = comp_name.segments.clone(); - segments.pop(); - segments.push(last.into()); - Path { - leading_colon: None, - segments, - } - }); ty = RouteType::Leaf { component: comp_name, - props: props_name, }; route = args.route.value(); } @@ -227,7 +208,6 @@ impl Route { pub fn routable_match(&self, layouts: &[Layout], nests: &[Nest]) -> TokenStream2 { let name = &self.route_name; - let name_str = name.to_string(); let mut tokens = TokenStream2::new(); @@ -261,16 +241,16 @@ impl Route { } } } - RouteType::Leaf { component, props } => { + RouteType::Leaf { component } => { let dynamic_segments = self.dynamic_segments(); let dynamic_segments_from_route = self.dynamic_segments(); quote! { #[allow(unused)] (#last_index, Self::#name { #(#dynamic_segments,)* }) => { - let comp = #props { #(#dynamic_segments_from_route,)* }; - let dynamic = cx.component(#component, comp, #name_str); render! { - dynamic + #component { + #(#dynamic_segments_from_route: #dynamic_segments_from_route,)* + } } } } @@ -364,5 +344,5 @@ impl Route { #[derive(Debug)] pub(crate) enum RouteType { Child(Field), - Leaf { component: Path, props: Path }, + Leaf { component: Path }, } diff --git a/packages/router/src/components/default_errors.rs b/packages/router/src/components/default_errors.rs index 704c6b972..1d45d0747 100644 --- a/packages/router/src/components/default_errors.rs +++ b/packages/router/src/components/default_errors.rs @@ -1,9 +1,11 @@ +#[allow(deprecated)] use crate::hooks::use_router; use dioxus::prelude::*; /// The default component to render when an external navigation fails. #[allow(non_snake_case)] pub fn FailureExternalNavigation(cx: Scope) -> Element { + #[allow(deprecated)] let router = use_router(cx); render! { diff --git a/packages/router/src/components/link.rs b/packages/router/src/components/link.rs index 15784016d..bc55fec8c 100644 --- a/packages/router/src/components/link.rs +++ b/packages/router/src/components/link.rs @@ -1,5 +1,6 @@ use std::any::Any; use std::fmt::Debug; +use std::rc::Rc; use dioxus::prelude::*; use log::error; @@ -9,25 +10,24 @@ use crate::prelude::Routable; use crate::utils::use_router_internal::use_router_internal; /// Something that can be converted into a [`NavigationTarget`]. +#[derive(Clone)] pub enum IntoRoutable { /// A raw string target. FromStr(String), /// A internal target. - Route(Box), + Route(Rc), } impl From for IntoRoutable { fn from(value: R) -> Self { - IntoRoutable::Route(Box::new(value) as Box) + IntoRoutable::Route(Rc::new(value) as Rc) } } impl From> for IntoRoutable { fn from(value: NavigationTarget) -> Self { match value { - NavigationTarget::Internal(route) => { - IntoRoutable::Route(Box::new(route) as Box) - } + NavigationTarget::Internal(route) => IntoRoutable::Route(Rc::new(route) as Rc), NavigationTarget::External(url) => IntoRoutable::FromStr(url), } } @@ -197,10 +197,7 @@ pub fn Link<'a>(cx: Scope<'a, LinkProps<'a>>) -> Element { IntoRoutable::FromStr(url) => url.to_string(), IntoRoutable::Route(route) => router.any_route_to_string(&**route), }; - let parsed_route: NavigationTarget> = match router.route_from_str(&href) { - Ok(route) => NavigationTarget::Internal(route), - Err(err) => NavigationTarget::External(err), - }; + let parsed_route: NavigationTarget> = router.resolve_into_routable(to.clone()); let ac = active_class .and_then(|active_class| (href == current_url).then(|| format!(" {active_class}"))) .unwrap_or_default(); @@ -219,7 +216,7 @@ pub fn Link<'a>(cx: Scope<'a, LinkProps<'a>>) -> Element { let do_default = onclick.is_none() || !onclick_only; let action = move |event| { if do_default && is_router_nav { - router.push_any(router.resolve_into_routable(to)); + router.push_any(router.resolve_into_routable(to.clone())); } if let Some(handler) = onclick { diff --git a/packages/router/src/contexts/navigator.rs b/packages/router/src/contexts/navigator.rs index d6c08f485..961122c05 100644 --- a/packages/router/src/contexts/navigator.rs +++ b/packages/router/src/contexts/navigator.rs @@ -35,17 +35,13 @@ impl Navigator { /// /// The previous location will be available to go back to. pub fn push(&self, target: impl Into) -> Option { - let target = target.into(); - let as_any_navigation_target = self.0.resolve_into_routable(&target); - self.0.push_any(as_any_navigation_target) + self.0.push(target) } /// Replace the current location. /// /// The previous location will **not** be available to go back to. pub fn replace(&self, target: impl Into) -> Option { - let target = target.into(); - let as_any_navigation_target = self.0.resolve_into_routable(&target); - self.0.replace_any(as_any_navigation_target) + self.0.replace(target) } } diff --git a/packages/router/src/contexts/router.rs b/packages/router/src/contexts/router.rs index 89008b0dd..e13d1cc20 100644 --- a/packages/router/src/contexts/router.rs +++ b/packages/router/src/contexts/router.rs @@ -1,6 +1,7 @@ use std::{ any::Any, collections::HashSet, + rc::Rc, sync::{Arc, RwLock, RwLockWriteGuard}, }; @@ -18,9 +19,10 @@ use crate::{ pub struct ExternalNavigationFailure(String); /// A function the router will call after every routing update. -pub(crate) type RoutingCallback = Arc) -> Option>>; +pub(crate) type RoutingCallback = + Arc) -> Option>>; pub(crate) type AnyRoutingCallback = - Arc Option>>>; + Arc Option>>>; struct MutableRouterState { /// The current prefix. @@ -70,18 +72,18 @@ impl RouterContext { routing_callback: cfg.on_update.map(|update| { Arc::new(move |ctx| { - let ctx = LinkContext { + let ctx = GenericRouterContext { inner: ctx, _marker: std::marker::PhantomData, }; update(ctx).map(|t| match t { NavigationTarget::Internal(r) => { - NavigationTarget::Internal(Box::new(r) as Box) + NavigationTarget::Internal(Rc::new(r) as Rc) } NavigationTarget::External(s) => NavigationTarget::External(s), }) }) - as Arc Option>>> + as Arc Option>>> }), failure_external_navigation: cfg.failure_external_navigation, @@ -114,7 +116,7 @@ impl RouterContext { myself } - pub(crate) fn route_from_str(&self, route: &str) -> Result, String> { + pub(crate) fn route_from_str(&self, route: &str) -> Result, String> { let state = self.state.read().unwrap(); state.history.parse_route(route) } @@ -155,7 +157,7 @@ impl RouterContext { pub(crate) fn push_any( &self, - target: NavigationTarget>, + target: NavigationTarget>, ) -> Option { match target { NavigationTarget::Internal(p) => { @@ -171,15 +173,12 @@ impl RouterContext { /// Push a new location. /// /// The previous location will be available to go back to. - pub fn push( - &self, - target: impl Into>, - ) -> Option { - let target = target.into(); + pub fn push(&self, target: impl Into) -> Option { + let target = self.resolve_into_routable(target.into()); match target { NavigationTarget::Internal(p) => { let mut state = self.state_mut(); - state.history.push(Box::new(p)) + state.history.push(Rc::new(p)) } NavigationTarget::External(e) => return self.external(e), } @@ -190,16 +189,13 @@ impl RouterContext { /// Replace the current location. /// /// The previous location will **not** be available to go back to. - pub fn replace( - &self, - target: impl Into>, - ) -> Option { - let target = target.into(); + pub fn replace(&self, target: impl Into) -> Option { + let target = self.resolve_into_routable(target.into()); { let mut state = self.state_mut(); match target { - NavigationTarget::Internal(p) => state.history.replace(Box::new(p)), + NavigationTarget::Internal(p) => state.history.replace(Rc::new(p)), NavigationTarget::External(e) => return self.external(e), } } @@ -207,31 +203,17 @@ impl RouterContext { self.change_route() } - pub(crate) fn replace_any( - &self, - target: NavigationTarget>, - ) -> Option { - match target { - NavigationTarget::Internal(p) => { - let mut state = self.state_mut(); - state.history.replace(p) - } - NavigationTarget::External(e) => return self.external(e), - } - - self.change_route() - } - /// The route that is currently active. pub fn current(&self) -> R { - *self - .state + self.state .read() .unwrap() .history .current_route() .downcast::() .unwrap() + .as_ref() + .clone() } /// The route that is currently active. @@ -245,17 +227,18 @@ impl RouterContext { pub(crate) fn resolve_into_routable( &self, - into_routable: &IntoRoutable, - ) -> NavigationTarget> { - let href = match into_routable { - IntoRoutable::FromStr(url) => url.to_string(), - IntoRoutable::Route(route) => self.any_route_to_string(&**route), - }; - let parsed_route: NavigationTarget> = match self.route_from_str(&href) { - Ok(route) => NavigationTarget::Internal(route), - Err(err) => NavigationTarget::External(err), - }; - parsed_route + into_routable: IntoRoutable, + ) -> NavigationTarget> { + match into_routable { + IntoRoutable::FromStr(url) => { + let parsed_route: NavigationTarget> = match self.route_from_str(&url) { + Ok(route) => NavigationTarget::Internal(route), + Err(err) => NavigationTarget::External(err), + }; + parsed_route + } + IntoRoutable::Route(route) => NavigationTarget::Internal(route), + } } /// The prefix that is currently active. @@ -332,12 +315,12 @@ impl RouterContext { } } -pub struct LinkContext { +pub struct GenericRouterContext { inner: RouterContext, _marker: std::marker::PhantomData, } -impl LinkContext +impl GenericRouterContext where R: Routable, { @@ -374,7 +357,7 @@ where &self, target: impl Into>, ) -> Option { - self.inner.push(target) + self.inner.push(target.into()) } /// Replace the current location. @@ -384,7 +367,7 @@ where &self, target: impl Into>, ) -> Option { - self.inner.replace(target) + self.inner.replace(target.into()) } /// The route that is currently active. diff --git a/packages/router/src/history/mod.rs b/packages/router/src/history/mod.rs index 4c5b2a3e9..fda386075 100644 --- a/packages/router/src/history/mod.rs +++ b/packages/router/src/history/mod.rs @@ -10,7 +10,7 @@ //! 1) [`MemoryHistory`] for desktop/mobile/ssr platforms //! 2) [`WebHistory`] for web platforms -use std::{any::Any, sync::Arc}; +use std::{any::Any, rc::Rc, sync::Arc}; mod memory; pub use memory::*; @@ -279,13 +279,13 @@ pub trait HistoryProvider { } pub(crate) trait AnyHistoryProvider { - fn parse_route(&self, route: &str) -> Result, String>; + fn parse_route(&self, route: &str) -> Result, String>; #[must_use] fn accepts_type_id(&self, type_id: &std::any::TypeId) -> bool; #[must_use] - fn current_route(&self) -> Box; + fn current_route(&self) -> Rc; #[must_use] fn current_prefix(&self) -> Option { @@ -306,9 +306,9 @@ pub(crate) trait AnyHistoryProvider { fn go_forward(&mut self); - fn push(&mut self, route: Box); + fn push(&mut self, route: Rc); - fn replace(&mut self, path: Box); + fn replace(&mut self, path: Rc); #[allow(unused_variables)] fn external(&mut self, url: String) -> bool { @@ -345,19 +345,19 @@ where ::Err: std::fmt::Display, H: HistoryProvider, { - fn parse_route(&self, route: &str) -> Result, String> { + fn parse_route(&self, route: &str) -> Result, String> { R::from_str(route) .map_err(|err| err.to_string()) - .map(|route| Box::new(route) as Box) + .map(|route| Rc::new(route) as Rc) } fn accepts_type_id(&self, type_id: &std::any::TypeId) -> bool { type_id == &std::any::TypeId::of::() } - fn current_route(&self) -> Box { + fn current_route(&self) -> Rc { let route = self.inner.current_route(); - Box::new(route) + Rc::new(route) } fn current_prefix(&self) -> Option { @@ -380,12 +380,14 @@ where self.inner.go_forward() } - fn push(&mut self, route: Box) { - self.inner.push(*route.downcast().unwrap()) + fn push(&mut self, route: Rc) { + self.inner + .push(route.downcast::().unwrap().as_ref().clone()) } - fn replace(&mut self, path: Box) { - self.inner.replace(*path.downcast().unwrap()) + fn replace(&mut self, route: Rc) { + self.inner + .replace(route.downcast::().unwrap().as_ref().clone()) } fn external(&mut self, url: String) -> bool { diff --git a/packages/router/src/hooks/use_router.rs b/packages/router/src/hooks/use_router.rs index 1b7556665..0891ebf97 100644 --- a/packages/router/src/hooks/use_router.rs +++ b/packages/router/src/hooks/use_router.rs @@ -2,6 +2,7 @@ use dioxus::prelude::ScopeState; use crate::{prelude::RouterContext, utils::use_router_internal::use_router_internal}; +#[deprecated = "prefer the use_navigator or use_route functions"] /// A hook that provides access to information about the router. pub fn use_router(cx: &ScopeState) -> &RouterContext { use_router_internal(cx) diff --git a/packages/router/src/lib.rs b/packages/router/src/lib.rs index 1310f64bd..49b7ffc42 100644 --- a/packages/router/src/lib.rs +++ b/packages/router/src/lib.rs @@ -42,7 +42,7 @@ mod history; /// Hooks for interacting with the router in components. pub mod hooks { mod use_router; - pub(crate) use use_router::*; + pub use use_router::*; mod use_route; pub use use_route::*; diff --git a/packages/router/src/router_cfg.rs b/packages/router/src/router_cfg.rs index 23731810c..764d70461 100644 --- a/packages/router/src/router_cfg.rs +++ b/packages/router/src/router_cfg.rs @@ -112,7 +112,7 @@ where /// Defaults to [`None`]. pub fn on_update( self, - callback: impl Fn(LinkContext) -> Option> + 'static, + callback: impl Fn(GenericRouterContext) -> Option> + 'static, ) -> Self { Self { on_update: Some(Arc::new(callback)),