dioxus_core/suspense/
mod.rs1mod component;
27pub use component::*;
28
29use crate::innerlude::*;
30use crate::mount::SuspenseBranch;
31use std::{
32 cell::{Cell, Ref, RefCell},
33 fmt::Debug,
34 rc::Rc,
35};
36
37#[derive(Clone, PartialEq, Debug)]
39pub struct SuspendedFuture {
40 origin: ScopeId,
41 task: TaskId,
42}
43
44impl SuspendedFuture {
45 pub fn new(task: Task) -> Self {
47 Self {
48 task: task.id,
49 origin: current_scope_id(),
50 }
51 }
52
53 pub fn task(&self) -> Task {
55 Task::from_id(self.task)
56 }
57}
58
59impl std::fmt::Display for SuspendedFuture {
60 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61 write!(f, "SuspendedFuture {{ task: {:?} }}", self.task)
62 }
63}
64
65#[derive(Debug, Clone)]
67pub struct SuspenseContext {
68 inner: Rc<SuspenseBoundaryInner>,
69}
70
71impl PartialEq for SuspenseContext {
72 fn eq(&self, other: &Self) -> bool {
73 Rc::ptr_eq(&self.inner, &other.inner)
74 }
75}
76
77impl SuspenseContext {
78 pub(crate) fn new() -> Self {
80 Self {
81 inner: Rc::new(SuspenseBoundaryInner {
82 rt: Runtime::current(),
83 suspended_tasks: RefCell::new(vec![]),
84 id: Cell::new(ScopeId::ROOT),
85 suspended_branch: Default::default(),
86 frozen: Default::default(),
87 after_suspense_resolved: Default::default(),
88 }),
89 }
90 }
91
92 pub(crate) fn mount(&self, scope: ScopeId) {
94 self.inner.id.set(scope);
95 }
96
97 pub fn suspended_nodes(&self) -> Option<VNode> {
99 self.suspended_branch().map(|branch| branch.root())
100 }
101
102 pub fn with_suspended_mounted_root<R>(
105 &self,
106 with_root: impl FnOnce(MountedVNode<'_>) -> R,
107 ) -> Option<R> {
108 let branch = self.inner.suspended_branch.borrow();
109 let branch = branch.as_ref()?;
110 Some(with_root(branch.mounted_root()))
111 }
112
113 pub(crate) fn suspended_branch(&self) -> Option<SuspenseBranch> {
115 self.inner.suspended_branch.borrow().clone()
116 }
117
118 pub(crate) fn set_suspended_branch(&self, branch: SuspenseBranch) {
120 self.inner.suspended_branch.borrow_mut().replace(branch);
121 }
122
123 pub(crate) fn take_suspended_branch(&self) -> Option<SuspenseBranch> {
125 self.inner.suspended_branch.borrow_mut().take()
126 }
127
128 pub fn frozen(&self) -> bool {
130 self.inner.frozen.get()
131 }
132
133 pub fn freeze(&self) {
135 self.inner.frozen.set(true);
136 }
137
138 pub fn has_suspended_tasks(&self) -> bool {
140 !self.inner.suspended_tasks.borrow().is_empty()
141 }
142
143 pub fn is_suspended(&self) -> bool {
145 self.inner.suspended_branch.borrow().is_some()
146 }
147
148 pub(crate) fn add_suspended_task(&self, task: SuspendedFuture) {
150 self.inner.suspended_tasks.borrow_mut().push(task);
151 self.inner.rt.needs_update(self.inner.id.get());
152 }
153
154 pub(crate) fn remove_suspended_task(&self, task: Task) {
156 self.inner
157 .suspended_tasks
158 .borrow_mut()
159 .retain(|t| t.task != task.id);
160 if let Some(scope) = self.inner.rt.try_get_state(self.inner.id.get()) {
161 scope.needs_update_any(scope.id);
162 }
163 }
164
165 pub fn suspended_futures(&self) -> Ref<'_, [SuspendedFuture]> {
167 Ref::map(self.inner.suspended_tasks.borrow(), |tasks| {
168 tasks.as_slice()
169 })
170 }
171
172 pub fn after_suspense_resolved(&self, callback: impl FnOnce() + 'static) {
174 let mut closures = self.inner.after_suspense_resolved.borrow_mut();
175 closures.push(Box::new(callback));
176 }
177
178 pub(crate) fn run_resolved_closures(&self, runtime: &Runtime) {
180 runtime.while_not_rendering(|| {
181 self.inner
182 .after_suspense_resolved
183 .borrow_mut()
184 .drain(..)
185 .for_each(|f| f());
186 })
187 }
188}
189
190pub(crate) struct SuspenseBoundaryInner {
192 rt: Rc<Runtime>,
193
194 suspended_tasks: RefCell<Vec<SuspendedFuture>>,
195
196 id: Cell<ScopeId>,
197
198 suspended_branch: RefCell<Option<SuspenseBranch>>,
200
201 frozen: Cell<bool>,
203
204 after_suspense_resolved: RefCell<Vec<Box<dyn FnOnce()>>>,
206}
207
208impl Debug for SuspenseBoundaryInner {
209 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
210 f.debug_struct("SuspenseBoundaryInner")
211 .field("suspended_tasks", &self.suspended_tasks)
212 .field("id", &self.id)
213 .field(
214 "has_suspended_branch",
215 &self.suspended_branch.borrow().is_some(),
216 )
217 .field("frozen", &self.frozen)
218 .finish()
219 }
220}