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}