diff --git a/packages/router/README.md b/packages/router/README.md index a6dc5027b..6fbc652eb 100644 --- a/packages/router/README.md +++ b/packages/router/README.md @@ -5,7 +5,7 @@ DioxusRouter adds React-Router style routing to your Dioxus apps. Works in brows ```rust fn app() { cx.render(rsx! { - Routes { + Router { Route { to: "/", Component {} }, Route { to: "/blog", Blog {} }, Route { to: "/blog/:id", BlogPost {} }, diff --git a/packages/router/src/components/router.rs b/packages/router/src/components/router.rs index 3d9f0c83a..240c3806a 100644 --- a/packages/router/src/components/router.rs +++ b/packages/router/src/components/router.rs @@ -11,17 +11,26 @@ use crate::RouterService; pub struct RouterProps<'a> { children: Element<'a>, - #[props(default, strip_option)] - onchange: Option<&'a dyn Fn(&'a str)>, + #[props(default)] + onchange: EventHandler<'a, String>, } #[allow(non_snake_case)] pub fn Router<'a>(cx: Scope<'a, RouterProps<'a>>) -> Element { - cx.use_hook(|_| { + let svc = cx.use_hook(|_| { let update = cx.schedule_update_any(); cx.provide_context(RouterService::new(update, cx.scope_id())) }); + let any_pending = svc.pending_events.borrow().len() > 0; + svc.pending_events.borrow_mut().clear(); + + if any_pending { + let location = svc.current_location(); + let path = location.path(); + cx.props.onchange.call(path.to_string()); + } + cx.render(rsx!( div { &cx.props.children } )) diff --git a/packages/router/src/lib.rs b/packages/router/src/lib.rs index b6ffb5971..309cef905 100644 --- a/packages/router/src/lib.rs +++ b/packages/router/src/lib.rs @@ -8,22 +8,9 @@ //! //! ```rust //! fn app(cx: Scope) -> Element { +//! //! } -//! -//! -//! -//! -//! //! ``` -//! -//! -//! -//! -//! -//! -//! -//! -//! mod hooks { mod use_route; diff --git a/packages/router/src/service.rs b/packages/router/src/service.rs index 00642414f..9da2bef7e 100644 --- a/packages/router/src/service.rs +++ b/packages/router/src/service.rs @@ -9,6 +9,7 @@ use dioxus_core::ScopeId; pub struct RouterService { pub(crate) regen_route: Rc, + pub(crate) pending_events: Rc>>, history: Rc>, slots: Rc>>, root_found: Rc>>, @@ -16,6 +17,12 @@ pub struct RouterService { listener: HistoryListener, } +pub enum RouteEvent { + Change, + Pop, + Push, +} + enum RouteSlot { Routes { // the partial route @@ -36,28 +43,37 @@ impl RouterService { let path = location.path(); let slots: Rc>> = Default::default(); - - let _slots = slots.clone(); - + let pending_events: Rc>> = Default::default(); let root_found = Rc::new(Cell::new(None)); - let regen = regen_route.clone(); - let _root_found = root_found.clone(); - let listener = history.listen(move || { - _root_found.set(None); - // checking if the route is valid is cheap, so we do it - for (slot, root) in _slots.borrow_mut().iter().rev() { - log::trace!("regenerating slot {:?} for root '{}'", slot, root); - regen(*slot); + + let listener = history.listen({ + let pending_events = pending_events.clone(); + let regen_route = regen_route.clone(); + let root_found = root_found.clone(); + let slots = slots.clone(); + move || { + root_found.set(None); + // checking if the route is valid is cheap, so we do it + for (slot, root) in slots.borrow_mut().iter().rev() { + log::trace!("regenerating slot {:?} for root '{}'", slot, root); + regen_route(*slot); + } + + // also regenerate the root + regen_route(root_scope); + + pending_events.borrow_mut().push(RouteEvent::Change) } }); Self { + listener, root_found, history: Rc::new(RefCell::new(history)), regen_route, slots, + pending_events, cur_path_params: Rc::new(RefCell::new(HashMap::new())), - listener, } }