dioxus_core/
scopes.rs

1use crate::{
2    any_props::BoxedAnyProps, nodes::AsVNode, reactive_context::ReactiveContext,
3    scope_context::Scope, Element, Runtime, VNode,
4};
5use std::{cell::Ref, rc::Rc};
6
7/// A component's unique identifier.
8///
9/// `ScopeId` is a `usize` that acts a key for the internal slab of Scopes. This means that the key is not unique across
10/// time. We do try and guarantee that between calls to `wait_for_work`, no ScopeIds will be recycled in order to give
11/// time for any logic that relies on these IDs to properly update.
12#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
13#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
14pub struct ScopeId(pub usize);
15
16impl std::fmt::Debug for ScopeId {
17    #[allow(unused_mut)]
18    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19        let mut builder = f.debug_tuple("ScopeId");
20        let mut builder = builder.field(&self.0);
21        #[cfg(debug_assertions)]
22        {
23            if let Some(name) = Runtime::current()
24                .ok()
25                .as_ref()
26                .and_then(|rt| rt.get_state(*self))
27            {
28                builder = builder.field(&name.name);
29            }
30        }
31        builder.finish()
32    }
33}
34
35impl ScopeId {
36    /// The ScopeId of the main scope passed into [`VirtualDom::new`].
37    ///
38    /// This scope will last for the entire duration of your app, making it convenient for long-lived state
39    /// that is created dynamically somewhere down the component tree.
40    ///
41    /// # Example
42    ///
43    /// ```rust, no_run
44    /// use dioxus::prelude::*;
45    /// let my_persistent_state = Signal::new_in_scope(String::new(), ScopeId::APP);
46    /// ```
47    // ScopeId(0) is the root scope wrapper
48    // ScopeId(1) is the default error boundary
49    // ScopeId(2) is the default suspense boundary
50    // ScopeId(3) is the users root scope
51    pub const APP: ScopeId = ScopeId(3);
52
53    /// The ScopeId of the topmost scope in the tree.
54    /// This will be higher up in the tree than [`ScopeId::APP`] because dioxus inserts a default [`SuspenseBoundary`] and [`ErrorBoundary`] at the root of the tree.
55    // ScopeId(0) is the root scope wrapper
56    pub const ROOT: ScopeId = ScopeId(0);
57
58    pub(crate) const PLACEHOLDER: ScopeId = ScopeId(usize::MAX);
59
60    pub(crate) fn is_placeholder(&self) -> bool {
61        *self == Self::PLACEHOLDER
62    }
63}
64
65/// A component's rendered state.
66///
67/// This state erases the type of the component's props. It is used to store the state of a component in the runtime.
68pub struct ScopeState {
69    pub(crate) runtime: Rc<Runtime>,
70    pub(crate) context_id: ScopeId,
71    /// The last node that has been rendered for this component. This node may not ben mounted
72    /// During suspense, this component can be rendered in the background multiple times
73    pub(crate) last_rendered_node: Option<Element>,
74    pub(crate) props: BoxedAnyProps,
75    pub(crate) reactive_context: ReactiveContext,
76}
77
78impl Drop for ScopeState {
79    fn drop(&mut self) {
80        self.runtime.remove_scope(self.context_id);
81    }
82}
83
84impl ScopeState {
85    /// Get a handle to the currently active head node arena for this Scope
86    ///
87    /// This is useful for traversing the tree outside of the VirtualDom, such as in a custom renderer or in SSR.
88    ///
89    /// Panics if the tree has not been built yet.
90    pub fn root_node(&self) -> &VNode {
91        self.try_root_node()
92            .expect("The tree has not been built yet. Make sure to call rebuild on the tree before accessing its nodes.")
93    }
94
95    /// Try to get a handle to the currently active head node arena for this Scope
96    ///
97    /// This is useful for traversing the tree outside of the VirtualDom, such as in a custom renderer or in SSR.
98    ///
99    /// Returns [`None`] if the tree has not been built yet.
100    pub fn try_root_node(&self) -> Option<&VNode> {
101        self.last_rendered_node.as_ref().map(AsVNode::as_vnode)
102    }
103
104    /// Returns the scope id of this [`ScopeState`].
105    pub fn id(&self) -> ScopeId {
106        self.context_id
107    }
108
109    pub(crate) fn state(&self) -> Ref<'_, Scope> {
110        self.runtime.get_state(self.context_id).unwrap()
111    }
112}