dioxus_router/contexts/
outlet.rs

1use dioxus_core::{provide_context, try_consume_context, use_hook, Element, VNode};
2
3use crate::{routable::Routable, utils::use_router_internal::use_router_internal};
4
5/// A context that manages nested routing levels for outlet components.
6///
7/// The outlet context keeps track of the current nesting level of routes and helps
8/// manage the hierarchical structure of nested routes in the application.
9///
10/// # Type Parameters
11///
12/// * `R` - The routable type that implements the routing logic
13#[derive(Clone, Default)]
14pub struct OutletContext<R> {
15    current_level: usize,
16    _marker: std::marker::PhantomData<R>,
17}
18
19impl<R> OutletContext<R> {
20    /// Creates a new outlet context starting at level 0
21    pub fn new() -> Self {
22        Self {
23            current_level: 0,
24            _marker: std::marker::PhantomData,
25        }
26    }
27
28    /// Creates a new outlet context for the next nesting level
29    pub fn next(&self) -> Self {
30        Self {
31            current_level: self.current_level + 1,
32            _marker: std::marker::PhantomData,
33        }
34    }
35
36    /// Returns the current nesting level of this outlet
37    pub fn level(&self) -> usize {
38        self.current_level
39    }
40
41    pub(crate) fn render() -> Element
42    where
43        R: Routable + Clone,
44    {
45        let router = use_router_internal().expect("Outlet must be inside of a router");
46        let outlet: OutletContext<R> = use_outlet_context();
47        let current_level = outlet.level();
48        provide_context(outlet.next());
49
50        if let Some(error) = router.render_error() {
51            return if current_level == 0 {
52                error
53            } else {
54                VNode::empty()
55            };
56        }
57
58        router.current::<R>().render(current_level)
59    }
60}
61
62/// Returns the current outlet context from the component hierarchy.
63///
64/// This hook retrieves the outlet context from the current component scope. If no context is found,
65/// it creates a new context with a default level of 0.
66///
67/// # Type Parameters
68///
69/// * `R` - The routable type that implements the routing logic
70///
71/// # Returns
72///
73/// Returns an [`OutletContext<R>`] containing the current nesting level information.
74///
75/// # Examples
76///
77/// ```rust, no_run
78/// # use dioxus::prelude::*;
79/// # use dioxus_router::use_outlet_context;
80///
81/// # #[derive(Routable,Clone,PartialEq)]
82/// # enum MyRouter {
83/// #   #[route("/")]
84/// #   MyView
85/// # }
86///
87/// # #[component]
88/// # fn MyView() -> Element {
89/// #   rsx!{ div { "My Text" } }
90/// # }
91///
92/// let outlet_ctx = use_outlet_context::<MyRouter>();
93/// println!("Current nesting level: {}", outlet_ctx.level());
94/// ```
95pub fn use_outlet_context<R: Clone + 'static>() -> OutletContext<R> {
96    use_hook(|| try_consume_context().unwrap_or_else(OutletContext::new))
97}