router.rs 4.3 KB

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