Browse Source

restore navigator

Evan Almloff 2 năm trước cách đây
mục cha
commit
fe601e2a48

+ 4 - 4
packages/router-macro/src/lib.rs

@@ -61,14 +61,14 @@ pub fn routable(input: TokenStream) -> TokenStream {
             dioxus_router::prelude::GenericGoForwardButton::<#name>(cx)
         }
 
-        #vis fn use_router(cx: &dioxus::prelude::ScopeState) -> &dioxus_router::prelude::GenericRouterContext<#name> {
-            dioxus_router::prelude::use_generic_router(cx)
-        }
-
         #vis fn use_route(cx: &dioxus::prelude::ScopeState) -> Option<#name> {
             dioxus_router::prelude::use_generic_route(cx)
         }
 
+        #vis fn use_navigator(cx: &dioxus::prelude::ScopeState) -> &dioxus_router::prelude::GenericNavigator<#name> {
+            dioxus_router::prelude::use_generic_navigator(cx)
+        }
+
         #error_type
 
         #parse_impl

+ 8 - 8
packages/router/examples/simple_routes.rs

@@ -79,10 +79,10 @@ fn Route2(cx: Scope, user_id: usize) -> Element {
 
 #[inline_props]
 fn Route3(cx: Scope, dynamic: String) -> Element {
-    let router = use_router(cx);
-    let router_route = router.current();
-    let current_route = use_ref(cx, String::new);
-    let parsed = Route::from_str(&current_route.read());
+    let navigator = use_navigator(cx);
+    let current_route = use_route(cx)?;
+    let current_route_str = use_ref(cx, String::new);
+    let parsed = Route::from_str(&current_route_str.read());
 
     let site_map = Route::SITE_MAP
         .iter()
@@ -92,9 +92,9 @@ fn Route3(cx: Scope, dynamic: String) -> Element {
     render! {
         input {
             oninput: move |evt| {
-                *current_route.write() = evt.value.clone();
+                *current_route_str.write() = evt.value.clone();
             },
-            value: "{current_route.read()}"
+            value: "{current_route_str.read()}"
         }
         "dynamic: {dynamic}"
         Link {
@@ -102,14 +102,14 @@ fn Route3(cx: Scope, dynamic: String) -> Element {
             "hello world link"
         }
         button {
-            onclick: move |_| { router.push(NavigationTarget::External("https://www.google.com".to_string())); },
+            onclick: move |_| { navigator.push(NavigationTarget::External("https://www.google.com".to_string())); },
             "google link"
         }
         p { "Site Map" }
         pre { "{site_map:#?}" }
         p { "Dynamic link" }
         if let Ok(route) = parsed {
-            if route != router_route {
+            if route != current_route {
                 render! {
                     Link {
                         target: route.clone(),

+ 53 - 0
packages/router/src/contexts/navigator.rs

@@ -0,0 +1,53 @@
+use crate::prelude::{ExternalNavigationFailure, GenericRouterContext, NavigationTarget, Routable};
+
+/// A view into the navigation state of a router.
+#[derive(Clone)]
+pub struct GenericNavigator<R: Routable>(pub(crate) GenericRouterContext<R>);
+
+impl<R: Routable> GenericNavigator<R> {
+    /// Check whether there is a previous page to navigate back to.
+    #[must_use]
+    pub fn can_go_back(&self) -> bool {
+        self.0.can_go_back()
+    }
+
+    /// Check whether there is a future page to navigate forward to.
+    #[must_use]
+    pub fn can_go_forward(&self) -> bool {
+        self.0.can_go_forward()
+    }
+
+    /// Go back to the previous location.
+    ///
+    /// Will fail silently if there is no previous location to go to.
+    pub fn go_back(&self) {
+        self.0.go_back();
+    }
+
+    /// Go back to the next location.
+    ///
+    /// Will fail silently if there is no next location to go to.
+    pub fn go_forward(&self) {
+        self.0.go_forward();
+    }
+
+    /// Push a new location.
+    ///
+    /// The previous location will be available to go back to.
+    pub fn push(
+        &self,
+        target: impl Into<NavigationTarget<R>>,
+    ) -> Option<ExternalNavigationFailure> {
+        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<NavigationTarget<R>>,
+    ) -> Option<ExternalNavigationFailure> {
+        self.0.replace(target)
+    }
+}

+ 62 - 0
packages/router/src/hooks/use_navigator.rs

@@ -0,0 +1,62 @@
+use dioxus::prelude::ScopeState;
+
+use crate::{
+    prelude::{GenericNavigator, GenericRouterContext},
+    routable::Routable,
+};
+
+/// A hook that provides access to the navigator to change the router history. Unlike [`use_router`], this hook will not cause a rerender when the current route changes
+///
+/// > The Routable macro will define a version of this hook with an explicit type.
+///
+/// ```rust
+/// # use dioxus::prelude::*;
+/// # use dioxus_router::prelude::*;
+/// # use serde::{Deserialize, Serialize};
+/// #[derive(Clone, Serialize, Deserialize, Routable)]
+/// enum Route {
+///     #[route("/")]
+///     Index {},
+///     #[route("/:id")]
+///     Dynamic { id: usize },
+/// }
+///
+/// fn App(cx: Scope) -> Element {
+///     render! {
+///         Router {}
+///     }
+/// }
+///
+/// #[inline_props]
+/// fn Index(cx: Scope) -> Element {
+///     let navigator = use_navigator(&cx);
+///
+///     render! {
+///         button {
+///             onclick: move |_| { navigator.push(Route::Dynamic { id: 1234 }); },
+///             "Go to /1234"
+///         }
+///     }
+/// }
+///
+/// #[inline_props]
+/// fn Dynamic(cx: Scope, id: usize) -> Element {
+///     render! {
+///         p {
+///             "Current ID: {id}"
+///         }
+///     }
+/// }
+///
+/// # let mut vdom = VirtualDom::new(App);
+/// # let _ = vdom.rebuild();
+/// ```
+pub fn use_generic_navigator<R: Routable + Clone>(cx: &ScopeState) -> &GenericNavigator<R> {
+    &*cx.use_hook(|| {
+        let router = cx
+            .consume_context::<GenericRouterContext<R>>()
+            .expect("Must be called in a descendant of a Router component");
+
+        GenericNavigator(router)
+    })
+}

+ 2 - 0
packages/router/src/hooks/use_route.rs

@@ -5,6 +5,8 @@ use crate::utils::use_router_internal::use_router_internal;
 
 /// A hook that provides access to information about the current routing location.
 ///
+/// > The Routable macro will define a version of this hook with an explicit type.
+///
 /// # Return values
 /// - None, when not called inside a [`GenericRouter`] component.
 /// - Otherwise the current route.

+ 1 - 44
packages/router/src/hooks/use_router.rs

@@ -5,50 +5,7 @@ use crate::{
     utils::use_router_internal::use_router_internal,
 };
 
-/// A hook that provides access to information about the router. The Router will define a version of this hook with an explicit type.
-///
-/// ```rust
-/// # use dioxus::prelude::*;
-/// # use dioxus_router::prelude::*;
-/// # use serde::{Deserialize, Serialize};
-/// #[derive(Clone, Serialize, Deserialize, Routable)]
-/// enum Route {
-///     #[route("/")]
-///     Index {},
-///     #[route("/:id")]
-///     Dynamic { id: usize },
-/// }
-///
-/// fn App(cx: Scope) -> Element {
-///     render! {
-///         Router {}
-///     }
-/// }
-///
-/// #[inline_props]
-/// fn Index(cx: Scope) -> Element {
-///     let router = use_router(&cx);
-///
-///     render! {
-///         button {
-///             onclick: move |_| { router.push(Route::Dynamic { id: 1234 }); },
-///             "Go to /1234"
-///         }
-///     }
-/// }
-///
-/// #[inline_props]
-/// fn Dynamic(cx: Scope, id: usize) -> Element {
-///     render! {
-///         p {
-///             "Current ID: {id}"
-///         }
-///     }
-/// }
-///
-/// # let mut vdom = VirtualDom::new(App);
-/// # let _ = vdom.rebuild();
-/// ```
+/// A hook that provides access to information about the router.
 pub fn use_generic_router<R: Routable + Clone>(cx: &ScopeState) -> &GenericRouterContext<R> {
     use_router_internal(cx)
         .as_ref()

+ 7 - 2
packages/router/src/lib.rs

@@ -25,9 +25,11 @@ mod components {
 }
 
 mod contexts {
+    pub(crate) mod navigator;
     pub(crate) mod outlet;
     pub(crate) mod router;
-    pub use router::*;
+    pub use navigator::*;
+    pub(crate) use router::*;
 }
 
 mod router_cfg;
@@ -37,10 +39,13 @@ mod history;
 /// Hooks for interacting with the router in components.
 mod hooks {
     mod use_router;
-    pub use use_router::*;
+    pub(crate) use use_router::*;
 
     mod use_route;
     pub use use_route::*;
+
+    mod use_navigator;
+    pub use use_navigator::*;
 }
 
 /// A collection of useful items most applications might need.