diff --git a/packages/router/examples/simple_routes.rs b/packages/router/examples/simple_routes.rs index c620b81a4..5ad1badd0 100644 --- a/packages/router/examples/simple_routes.rs +++ b/packages/router/examples/simple_routes.rs @@ -14,18 +14,7 @@ fn main() { fn root(cx: Scope) -> Element { render! { - Router { - config: RouterConfiguration { - history: { - #[cfg(not(target_arch = "wasm32"))] - let history = Box::>::default(); - #[cfg(target_arch = "wasm32")] - let history = Box::>::default(); - history - }, - ..Default::default() - } - } + Router {} } } diff --git a/packages/router/src/components/router.rs b/packages/router/src/components/router.rs index a5299d08a..aeab7b815 100644 --- a/packages/router/src/components/router.rs +++ b/packages/router/src/components/router.rs @@ -9,25 +9,26 @@ use crate::{ }; /// The config for [`GenericRouter`]. -pub struct RouterCfg { - config: RefCell>>, +pub struct RouterConfigFactory { + #[allow(clippy::type_complexity)] + config: RefCell RouterConfiguration>>>, } -impl Default for RouterCfg +impl Default for RouterConfigFactory where ::Err: std::fmt::Display, { fn default() -> Self { - Self { - config: RefCell::new(Some(RouterConfiguration::default())), - } + Self::from(RouterConfiguration::default) } } -impl From> for RouterCfg { - fn from(value: RouterConfiguration) -> Self { +impl RouterConfiguration + 'static> From + for RouterConfigFactory +{ + fn from(value: F) -> Self { Self { - config: RefCell::new(Some(value)), + config: RefCell::new(Some(Box::new(value))), } } } @@ -39,7 +40,7 @@ where ::Err: std::fmt::Display, { #[props(default, into)] - config: RouterCfg, + config: RouterConfigFactory, } impl PartialEq for GenericRouterProps @@ -66,7 +67,11 @@ where panic!("{}", msg); } let router = GenericRouterContext::new( - cx.props.config.config.take().unwrap_or_default(), + (cx.props + .config + .config + .take() + .expect("use_context_provider ran twice"))(), cx.schedule_update_any(), ); router diff --git a/packages/router/src/contexts/router.rs b/packages/router/src/contexts/router.rs index abb85e712..1bc320f13 100644 --- a/packages/router/src/contexts/router.rs +++ b/packages/router/src/contexts/router.rs @@ -15,7 +15,7 @@ use crate::{ pub struct ExternalNavigationFailure(String); /// A function the router will call after every routing update. -pub type RoutingCallback = Arc) -> Option>>; +pub(crate) type RoutingCallback = Arc) -> Option>>; struct MutableRouterState where diff --git a/packages/router/src/router_cfg.rs b/packages/router/src/router_cfg.rs index a11bb31a7..46b3df2df 100644 --- a/packages/router/src/router_cfg.rs +++ b/packages/router/src/router_cfg.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use crate::contexts::router::RoutingCallback; use crate::history::HistoryProvider; use crate::routable::Routable; @@ -7,7 +9,7 @@ use crate::prelude::*; /// Global configuration options for the router. /// -/// This implements [`Default`], so you can use it like this: +/// This implements [`Default`] and follows the builder pattern, so you can use it like this: /// ```rust,no_run /// # use dioxus_router::prelude::*; /// # use serde::{Deserialize, Serialize}; @@ -21,20 +23,34 @@ use crate::prelude::*; /// #[route("/")] /// Index {}, /// } -/// let cfg = RouterConfiguration { -/// history: Box::>::default(), -/// ..Default::default() -/// }; +/// let cfg = RouterConfiguration::default().history(WebHistory::default()); /// ``` pub struct RouterConfiguration { - /// A component to render when an external navigation fails. - /// - /// Defaults to a router-internal component called [`FailureExternalNavigation`] - pub failure_external_navigation: fn(Scope) -> Element, - /// The [`HistoryProvider`] the router should use. - /// - /// Defaults to a default [`MemoryHistory`]. - pub history: Box>, + pub(crate) failure_external_navigation: fn(Scope) -> Element, + pub(crate) history: Box>, + pub(crate) on_update: Option>, +} + +impl Default for RouterConfiguration +where + ::Err: std::fmt::Display, +{ + fn default() -> Self { + Self { + failure_external_navigation: FailureExternalNavigation::, + history: { + #[cfg(all(target_arch = "wasm32", feature = "web"))] + let history = Box::>::default(); + #[cfg(not(all(target_arch = "wasm32", feature = "web")))] + let history = Box::>::default(); + history + }, + on_update: None, + } + } +} + +impl RouterConfiguration { /// A function to be called whenever the routing is updated. /// /// The callback is invoked after the routing is updated, but before components and hooks are @@ -48,18 +64,33 @@ pub struct RouterConfiguration { /// navigation failure occurs. /// /// Defaults to [`None`]. - pub on_update: Option>, -} - -impl Default for RouterConfiguration -where - ::Err: std::fmt::Display, -{ - fn default() -> Self { + pub fn on_update( + self, + callback: impl Fn(GenericRouterContext) -> Option> + 'static, + ) -> Self { Self { - failure_external_navigation: FailureExternalNavigation::, - history: Box::>::default(), - on_update: None, + on_update: Some(Arc::new(callback)), + ..self + } + } + + /// The [`HistoryProvider`] the router should use. + /// + /// Defaults to a default [`MemoryHistory`]. + pub fn history(self, history: impl HistoryProvider + 'static) -> Self { + Self { + history: Box::new(history), + ..self + } + } + + /// A component to render when an external navigation fails. + /// + /// Defaults to a router-internal component called [`FailureExternalNavigation`] + pub fn failure_external_navigation(self, component: fn(Scope) -> Element) -> Self { + Self { + failure_external_navigation: component, + ..self } } } diff --git a/packages/router/tests/via_ssr/link.rs b/packages/router/tests/via_ssr/link.rs index 7f15a9cc7..3b0730435 100644 --- a/packages/router/tests/via_ssr/link.rs +++ b/packages/router/tests/via_ssr/link.rs @@ -1,10 +1,9 @@ #![allow(non_snake_case)] use dioxus::prelude::*; use dioxus_router::prelude::*; -use serde::{de::DeserializeOwned, Deserialize, Serialize}; use std::str::FromStr; -fn prepare() -> String +fn prepare() -> String where ::Err: std::fmt::Display, { @@ -28,17 +27,14 @@ where } } - fn App(cx: Scope>) -> Element + fn App(cx: Scope>) -> Element where ::Err: std::fmt::Display, { render! { h1 { "App" } GenericRouter:: { - config: RouterConfiguration { - history: Box::>::default(), - ..Default::default() - } + config: || RouterConfiguration::default().history(MemoryHistory::default()) } } } @@ -46,7 +42,7 @@ where #[test] fn href_internal() { - #[derive(Routable, Clone, Serialize, Deserialize)] + #[derive(Routable, Clone)] enum Route { #[route("/")] Root {}, @@ -84,7 +80,7 @@ fn href_internal() { #[test] fn href_external() { - #[derive(Routable, Clone, Serialize, Deserialize)] + #[derive(Routable, Clone)] enum Route { #[route("/")] Root {}, @@ -122,7 +118,7 @@ fn href_external() { #[test] fn with_class() { - #[derive(Routable, Clone, Serialize, Deserialize)] + #[derive(Routable, Clone)] enum Route { #[route("/")] Root {}, @@ -161,7 +157,7 @@ fn with_class() { #[test] fn with_active_class_active() { - #[derive(Routable, Clone, Serialize, Deserialize)] + #[derive(Routable, Clone)] enum Route { #[route("/")] Root {}, @@ -194,7 +190,7 @@ fn with_active_class_active() { #[test] fn with_active_class_inactive() { - #[derive(Routable, Clone, Serialize, Deserialize)] + #[derive(Routable, Clone)] enum Route { #[route("/")] Root {}, @@ -234,7 +230,7 @@ fn with_active_class_inactive() { #[test] fn with_id() { - #[derive(Routable, Clone, Serialize, Deserialize)] + #[derive(Routable, Clone)] enum Route { #[route("/")] Root {}, @@ -273,7 +269,7 @@ fn with_id() { #[test] fn with_new_tab() { - #[derive(Routable, Clone, Serialize, Deserialize)] + #[derive(Routable, Clone)] enum Route { #[route("/")] Root {}, @@ -312,7 +308,7 @@ fn with_new_tab() { #[test] fn with_new_tab_external() { - #[derive(Routable, Clone, Serialize, Deserialize)] + #[derive(Routable, Clone)] enum Route { #[route("/")] Root {}, @@ -344,7 +340,7 @@ fn with_new_tab_external() { #[test] fn with_rel() { - #[derive(Routable, Clone, Serialize, Deserialize)] + #[derive(Routable, Clone)] enum Route { #[route("/")] Root {}, diff --git a/packages/router/tests/via_ssr/outlet.rs b/packages/router/tests/via_ssr/outlet.rs index dab2e60f4..3378b5ddd 100644 --- a/packages/router/tests/via_ssr/outlet.rs +++ b/packages/router/tests/via_ssr/outlet.rs @@ -2,14 +2,13 @@ use dioxus::prelude::*; use dioxus_router::prelude::*; -use serde::{Deserialize, Serialize}; fn prepare(path: impl Into) -> VirtualDom { let mut vdom = VirtualDom::new_with_props(App, AppProps { path: path.into() }); let _ = vdom.rebuild(); return vdom; - #[derive(Routable, Clone, Serialize, Deserialize)] + #[derive(Routable, Clone)] #[rustfmt::skip] enum Route { #[route("/")] @@ -36,15 +35,13 @@ fn prepare(path: impl Into) -> VirtualDom { } fn App(cx: Scope) -> Element { - let cfg = RouterConfiguration { - history: Box::new(MemoryHistory::with_initial_path(cx.props.path.clone()).unwrap()), - ..Default::default() - }; - render! { h1 { "App" } Router { - config: cfg + config: { + let path = cx.props.path.clone(); + move || RouterConfiguration::default().history(MemoryHistory::with_initial_path(path).unwrap()) + } } } }