router.rs 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. use crate::ParsedRoute;
  2. use crate::{cfg::RouterCfg, RouteEvent, RouterCore};
  3. use dioxus_core as dioxus;
  4. use dioxus_core::prelude::*;
  5. use dioxus_core_macro::*;
  6. use dioxus_html as dioxus_elements;
  7. use futures_util::stream::StreamExt;
  8. use std::sync::Arc;
  9. /// The props for the [`Router`](fn.Router.html) component.
  10. #[derive(Props)]
  11. pub struct RouterProps<'a> {
  12. /// The routes and elements that should be rendered when the path matches.
  13. ///
  14. /// If elements are not contained within Routes, the will be rendered
  15. /// regardless of the path.
  16. pub children: Element<'a>,
  17. /// The URL to point at
  18. ///
  19. /// This will be used to trim any latent segments from the URL when your app is
  20. /// not deployed to the root of the domain.
  21. pub base_url: Option<&'a str>,
  22. /// Hook into the router when the route is changed.
  23. ///
  24. /// This lets you easily implement redirects
  25. #[props(default)]
  26. pub onchange: EventHandler<'a, Arc<RouterCore>>,
  27. /// Set the active class of all Link components contained in this router.
  28. ///
  29. /// This is useful if you don't want to repeat the same `active_class` prop value in every Link.
  30. /// By default set to `"active"`.
  31. pub active_class: Option<&'a str>,
  32. }
  33. /// A component that conditionally renders children based on the current location of the app.
  34. ///
  35. /// Uses BrowserRouter in the browser and HashRouter everywhere else.
  36. ///
  37. /// Will fallback to HashRouter is BrowserRouter is not available, or through configuration.
  38. #[allow(non_snake_case)]
  39. pub fn Router<'a>(cx: Scope<'a, RouterProps<'a>>) -> Element {
  40. let svc = cx.use_hook(|_| {
  41. let (tx, mut rx) = futures_channel::mpsc::unbounded::<RouteEvent>();
  42. let svc = RouterCore::new(
  43. tx,
  44. RouterCfg {
  45. base_url: cx.props.base_url.map(|s| s.to_string()),
  46. active_class: cx.props.active_class.map(|s| s.to_string()),
  47. },
  48. );
  49. cx.spawn({
  50. let svc = svc.clone();
  51. let regen_route = cx.schedule_update_any();
  52. let router_id = cx.scope_id();
  53. async move {
  54. while let Some(msg) = rx.next().await {
  55. match msg {
  56. RouteEvent::Push {
  57. route,
  58. serialized_state,
  59. title,
  60. } => {
  61. let new_route = Arc::new(ParsedRoute {
  62. url: svc.current_location().url.join(&route).ok().unwrap(),
  63. title,
  64. serialized_state,
  65. });
  66. svc.history.push(&new_route);
  67. svc.stack.borrow_mut().push(new_route);
  68. }
  69. RouteEvent::Replace {
  70. route,
  71. title,
  72. serialized_state,
  73. } => {
  74. let new_route = Arc::new(ParsedRoute {
  75. url: svc.current_location().url.join(&route).ok().unwrap(),
  76. title,
  77. serialized_state,
  78. });
  79. svc.history.replace(&new_route);
  80. *svc.stack.borrow_mut().last_mut().unwrap() = new_route;
  81. }
  82. RouteEvent::Pop => {
  83. let mut stack = svc.stack.borrow_mut();
  84. if stack.len() == 1 {
  85. continue;
  86. }
  87. stack.pop();
  88. }
  89. }
  90. svc.route_found.set(None);
  91. regen_route(router_id);
  92. for listener in svc.onchange_listeners.borrow().iter() {
  93. regen_route(*listener);
  94. }
  95. for route in svc.ordering.borrow().iter().rev() {
  96. regen_route(*route);
  97. }
  98. }
  99. }
  100. });
  101. cx.provide_context(svc)
  102. });
  103. // next time we run the rout_found will be filled
  104. if svc.route_found.get().is_none() {
  105. cx.props.onchange.call(svc.clone());
  106. }
  107. cx.render(rsx!(&cx.props.children))
  108. }