yew/suspense/
component.rs1use crate::html::{Html, Properties};
2
3#[derive(Properties, PartialEq, Debug, Clone)]
5pub struct SuspenseProps {
6 #[prop_or_default]
8 pub children: Html,
9
10 #[prop_or_default]
12 pub fallback: Html,
13}
14
15#[cfg(any(feature = "csr", feature = "ssr"))]
16mod feat_csr_ssr {
17 use super::*;
18 use crate::html::{Component, Context, Html, Scope};
19 use crate::suspense::Suspension;
20 #[cfg(feature = "hydration")]
21 use crate::suspense::SuspensionHandle;
22 use crate::virtual_dom::{VNode, VSuspense};
23 use crate::{component, html};
24
25 #[derive(Properties, PartialEq, Debug, Clone)]
26 pub(crate) struct BaseSuspenseProps {
27 pub children: Html,
28 #[prop_or(None)]
29 pub fallback: Option<Html>,
30 }
31
32 #[derive(Debug)]
33 pub(crate) enum BaseSuspenseMsg {
34 Suspend(Suspension),
35 Resume(Suspension),
36 }
37
38 #[derive(Debug)]
39 pub(crate) struct BaseSuspense {
40 suspensions: Vec<Suspension>,
41 #[cfg(feature = "hydration")]
42 hydration_handle: Option<SuspensionHandle>,
43 }
44
45 impl Component for BaseSuspense {
46 type Message = BaseSuspenseMsg;
47 type Properties = BaseSuspenseProps;
48
49 fn create(_ctx: &Context<Self>) -> Self {
50 #[cfg(not(feature = "hydration"))]
51 let suspensions = Vec::new();
52
53 #[cfg(feature = "hydration")]
55 let (suspensions, hydration_handle) = {
56 use crate::callback::Callback;
57 use crate::html::RenderMode;
58
59 match _ctx.creation_mode() {
60 RenderMode::Hydration => {
61 let link = _ctx.link().clone();
62 let (s, handle) = Suspension::new();
63 s.listen(Callback::from(move |s| {
64 link.send_message(BaseSuspenseMsg::Resume(s));
65 }));
66 (vec![s], Some(handle))
67 }
68 _ => (Vec::new(), None),
69 }
70 };
71
72 Self {
73 suspensions,
74 #[cfg(feature = "hydration")]
75 hydration_handle,
76 }
77 }
78
79 fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
80 match msg {
81 Self::Message::Suspend(m) => {
82 assert!(
83 ctx.props().fallback.is_some(),
84 "You cannot suspend from a component rendered as a fallback."
85 );
86
87 if m.resumed() {
88 return false;
89 }
90
91 if self.suspensions.iter().any(|n| n == &m) {
93 return false;
94 }
95
96 self.suspensions.push(m);
97
98 true
99 }
100 Self::Message::Resume(ref m) => {
101 let suspensions_len = self.suspensions.len();
102 self.suspensions.retain(|n| m != n);
103
104 suspensions_len != self.suspensions.len()
105 }
106 }
107 }
108
109 fn view(&self, ctx: &Context<Self>) -> Html {
110 let BaseSuspenseProps { children, fallback } = (*ctx.props()).clone();
111 let children = VNode::VList(::std::rc::Rc::new(
112 crate::virtual_dom::VList::with_children(vec![children], None),
113 ));
114
115 match fallback {
116 Some(fallback) => {
117 let vsuspense = VSuspense::new(
118 children,
119 fallback,
120 !self.suspensions.is_empty(),
121 None,
123 );
124
125 VNode::from(vsuspense)
126 }
127 None => children,
128 }
129 }
130
131 #[cfg(feature = "hydration")]
132 fn rendered(&mut self, _ctx: &Context<Self>, first_render: bool) {
133 if first_render {
134 if let Some(m) = self.hydration_handle.take() {
135 m.resume();
136 }
137 }
138 }
139 }
140
141 impl BaseSuspense {
142 pub(crate) fn suspend(scope: &Scope<Self>, s: Suspension) {
143 scope.send_message(BaseSuspenseMsg::Suspend(s));
144 }
145
146 pub(crate) fn resume(scope: &Scope<Self>, s: Suspension) {
147 scope.send_message(BaseSuspenseMsg::Resume(s));
148 }
149 }
150
151 #[component]
153 pub fn Suspense(props: &SuspenseProps) -> Html {
154 let SuspenseProps { children, fallback } = props.clone();
155
156 let fallback = html! {
157 <BaseSuspense>
158 {fallback}
159 </BaseSuspense>
160 };
161
162 html! {
163 <BaseSuspense {fallback}>
164 {children}
165 </BaseSuspense>
166 }
167 }
168}
169
170#[cfg(any(feature = "csr", feature = "ssr"))]
171pub use feat_csr_ssr::*;
172
173#[cfg(not(any(feature = "ssr", feature = "csr")))]
174mod feat_no_csr_ssr {
175 use super::*;
176 use crate::component;
177
178 #[component]
180 pub fn Suspense(_props: &SuspenseProps) -> Html {
181 Html::default()
182 }
183}
184
185#[cfg(not(any(feature = "ssr", feature = "csr")))]
186pub use feat_no_csr_ssr::*;