1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
use crate::{
Element, MountedVNode, RenderError, Runtime, VNode,
innerlude::{BoxedAnyProps, MountId},
reactive_context::ReactiveContext,
scope_context::Scope,
};
use std::{cell::Ref, rc::Rc};
/// A component's unique identifier.
///
/// `ScopeId` is a `usize` that acts a key for the internal slab of Scopes. This means that the key is not unique across
/// time. We do try and guarantee that between calls to `wait_for_work`, no ScopeIds will be recycled in order to give
/// time for any logic that relies on these IDs to properly update.
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct ScopeId(usize);
impl std::fmt::Debug for ScopeId {
#[allow(unused_mut)]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut builder = f.debug_tuple("ScopeId");
let mut builder = builder.field(&self.0);
#[cfg(debug_assertions)]
{
if let Some(scope) = Runtime::try_current()
.as_ref()
.and_then(|r| r.try_get_state(*self))
{
builder = builder.field(&scope.name);
}
}
builder.finish()
}
}
impl ScopeId {
/// The ScopeId of the main scope passed into [`crate::VirtualDom::new`].
///
/// This scope will last for the entire duration of your app, making it convenient for long-lived state
/// that is created dynamically somewhere down the component tree.
///
/// # Example
///
/// ```rust, no_run
/// use dioxus::prelude::*;
/// let my_persistent_state = Signal::new_in_scope(String::new(), ScopeId::APP);
/// ```
// ScopeId(0) is the root scope wrapper
// ScopeId(1) is the default suspense boundary
// ScopeId(2) is the default error boundary
// ScopeId(3) is the users root scope
pub const APP: ScopeId = ScopeId(3);
/// The ScopeId of the topmost error boundary in the tree.
pub const ROOT_ERROR_BOUNDARY: ScopeId = ScopeId(2);
/// The ScopeId of the topmost suspense boundary in the tree.
pub const ROOT_SUSPENSE_BOUNDARY: ScopeId = ScopeId(1);
/// The ScopeId of the topmost scope in the tree.
/// This will be higher up in the tree than [`ScopeId::APP`] because dioxus inserts a default [`crate::SuspenseBoundary`] and [`crate::ErrorBoundary`] at the root of the tree.
// ScopeId(0) is the root scope wrapper
pub const ROOT: ScopeId = ScopeId(0);
pub(crate) const fn new(index: usize) -> Self {
Self(index)
}
pub(crate) const fn index(self) -> usize {
self.0
}
}
/// A component's rendered state.
///
/// This state erases the type of the component's props. It is used to store the state of a component in the runtime.
pub struct ScopeState {
pub(crate) runtime: Rc<Runtime>,
pub(crate) context_id: ScopeId,
pub(crate) props: BoxedAnyProps,
/// The last mounted output for this component.
pub(crate) last_rendered_node: Option<MountedOutput>,
pub(crate) reactive_context: ReactiveContext,
}
impl ScopeState {
/// Get a handle to the currently active head node arena for this Scope
///
/// This is useful for traversing the tree outside of the VirtualDom, such as in a custom renderer or in SSR.
///
/// Panics if the tree has not been built yet.
pub fn root_node(&self) -> &VNode {
self.try_root_node()
.expect("The tree has not been built yet. Make sure to call rebuild on the tree before accessing its nodes.")
}
/// Try to get a handle to the currently active head node arena for this Scope
///
/// This is useful for traversing the tree outside of the VirtualDom, such as in a custom renderer or in SSR.
///
/// Returns [`None`] if the tree has not been built yet.
pub fn try_root_node(&self) -> Option<&VNode> {
match &self.last_rendered_node {
Some(output) => Some(output.as_vnode()),
None => None,
}
}
/// Try to get the currently active root node with its mounted identity.
///
/// Returns [`None`] if the tree has not been built or mounted yet.
pub fn try_mounted_root_node(&self) -> Option<MountedVNode<'_>> {
self.last_rendered_node
.as_ref()
.map(MountedOutput::mounted_vnode)
}
/// Returns the scope id of this [`ScopeState`].
pub fn id(&self) -> ScopeId {
self.context_id
}
pub(crate) fn state(&self) -> Ref<'_, Scope> {
self.runtime.get_state(self.context_id)
}
/// Returns the height of this scope in the tree.
pub fn height(&self) -> u32 {
self.state().height()
}
}
#[derive(Clone, PartialEq, Debug)]
pub(crate) struct MountedOutput {
node: LastRenderedNode,
root_mount: MountId,
}
impl MountedOutput {
pub(crate) fn new(node: LastRenderedNode, root_mount: MountId) -> Self {
Self { node, root_mount }
}
pub(crate) fn node(&self) -> &LastRenderedNode {
&self.node
}
pub(crate) fn root_mount(&self) -> MountId {
self.root_mount
}
pub(crate) fn as_vnode(&self) -> &VNode {
self.node.as_vnode()
}
pub(crate) fn mounted_vnode(&self) -> MountedVNode<'_> {
MountedVNode::new(self.as_vnode(), self.root_mount)
}
}
impl std::ops::Deref for MountedOutput {
type Target = LastRenderedNode;
fn deref(&self) -> &Self::Target {
&self.node
}
}
#[derive(Clone, PartialEq, Debug)]
pub(crate) enum LastRenderedNode {
Real(VNode),
Placeholder(VNode, RenderError),
}
impl std::ops::Deref for LastRenderedNode {
type Target = VNode;
fn deref(&self) -> &Self::Target {
match self {
LastRenderedNode::Real(vnode) => vnode,
LastRenderedNode::Placeholder(vnode, _err) => vnode,
}
}
}
impl LastRenderedNode {
pub(crate) fn new(node: Element) -> Self {
match node {
Ok(vnode) => LastRenderedNode::Real(vnode),
Err(err) => LastRenderedNode::Placeholder(VNode::placeholder(), err),
}
}
pub(crate) fn as_vnode(&self) -> &VNode {
match self {
LastRenderedNode::Real(vnode) => vnode,
LastRenderedNode::Placeholder(vnode, _err) => vnode,
}
}
}
impl Drop for ScopeState {
fn drop(&mut self) {
self.runtime.remove_scope(self.context_id);
}
}