diff --git a/packages/yew-router/src/router.rs b/packages/yew-router/src/router.rs index 785ad6d36b3..8c2e2bca7d9 100644 --- a/packages/yew-router/src/router.rs +++ b/packages/yew-router/src/router.rs @@ -1,4 +1,5 @@ //! Router Component. +use std::rc::Rc; use crate::prelude::*; use yew::prelude::*; @@ -30,78 +31,65 @@ impl PartialEq for RouterState { } } -#[doc(hidden)] -pub enum Msg { - ReRender, +pub(crate) enum RouterStateAction { + Navigate, + ReplaceHistory(AnyHistory), } -/// The Router component. -/// -/// This provides [`History`] context to its children and switches. -/// -/// You only need one `` for each application. -pub struct Router { - _listener: HistoryListener, - history: AnyHistory, - ctr: u32, -} +impl Reducible for RouterState { + type Action = RouterStateAction; -impl Component for Router { - type Message = Msg; - type Properties = RouterProps; - - fn create(ctx: &Context) -> Self { - let link = ctx.link().clone(); - - let listener = ctx - .props() - .history - .listen(move || link.send_message(Msg::ReRender)); + fn reduce(self: Rc, action: Self::Action) -> Rc { + let history = match action { + RouterStateAction::Navigate => self.history(), + RouterStateAction::ReplaceHistory(m) => m, + }; Self { - _listener: listener, - history: ctx.props().history.clone(), - ctr: 0, + history, + ctr: self.ctr + 1, } + .into() } +} - fn update(&mut self, _ctx: &Context, msg: Self::Message) -> bool { - match msg { - Msg::ReRender => { - self.ctr += 1; - true - } - } - } - - fn changed(&mut self, ctx: &Context) -> bool { - let link = ctx.link().clone(); - - if self.history != ctx.props().history { - self._listener = ctx - .props() - .history - .listen(move || link.send_message(Msg::ReRender)); - - self.history = ctx.props().history.clone(); - - true - } else { - false - } +/// The Router component. +/// +/// This provides [`History`] context to its children and switches. +/// +/// You only need one `` for each application. +#[function_component(Router)] +pub fn router(props: &RouterProps) -> Html { + let RouterProps { history, children } = props.clone(); + + let state = use_reducer(|| RouterState { + history: history.clone(), + ctr: 0, + }); + + { + let state_dispatcher = state.dispatcher(); + + use_effect_with_deps( + move |history| { + state_dispatcher.dispatch(RouterStateAction::ReplaceHistory(history.clone())); + + let listener = + history.listen(move || state_dispatcher.dispatch(RouterStateAction::Navigate)); + + // We hold the listener in the destructor. + move || { + std::mem::drop(listener); + } + }, + history, + ); } - fn view(&self, ctx: &Context) -> Html { - let state = RouterState { - history: self.history.clone().into_any_history(), - ctr: self.ctr, - }; - - html! { - context={state}> - {ctx.props().children.clone()} - > - } + html! { + context={(*state).clone()}> + {children} + > } }