forked from DioxusLabs/dioxus
-
Notifications
You must be signed in to change notification settings - Fork 0
/
memory-leak.rs
254 lines (223 loc) · 8.73 KB
/
memory-leak.rs
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
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
#![allow(non_snake_case)]
use dioxus::prelude::*;
use dioxus_native_core::exports::shipyard::Component;
use dioxus_native_core::node_ref::*;
use dioxus_native_core::prelude::*;
use dioxus_native_core_macro::partial_derive_state;
struct FontSize(f64);
// All states need to derive Component
#[derive(Default, Debug, Copy, Clone, Component)]
struct Size(f64, f64);
/// Derive some of the boilerplate for the State implementation
#[partial_derive_state]
impl State for Size {
type ParentDependencies = ();
// The size of the current node depends on the size of its children
type ChildDependencies = (Self,);
type NodeDependencies = ();
// Size only cares about the width, height, and text parts of the current node
const NODE_MASK: NodeMaskBuilder<'static> = NodeMaskBuilder::new()
// Get access to the width and height attributes
.with_attrs(AttributeMaskBuilder::Some(&["width", "height"]))
// Get access to the text of the node
.with_text();
fn update<'a>(
&mut self,
node_view: NodeView<()>,
_node: <Self::NodeDependencies as Dependancy>::ElementBorrowed<'a>,
_parent: Option<<Self::ParentDependencies as Dependancy>::ElementBorrowed<'a>>,
children: Vec<<Self::ChildDependencies as Dependancy>::ElementBorrowed<'a>>,
context: &SendAnyMap,
) -> bool {
let font_size = context.get::<FontSize>().unwrap().0;
let mut width;
let mut height;
if let Some(text) = node_view.text() {
// if the node has text, use the text to size our object
width = text.len() as f64 * font_size;
height = font_size;
} else {
// otherwise, the size is the maximum size of the children
width = children
.iter()
.map(|(item,)| item.0)
.reduce(|accum, item| if accum >= item { accum } else { item })
.unwrap_or(0.0);
height = children
.iter()
.map(|(item,)| item.1)
.reduce(|accum, item| if accum >= item { accum } else { item })
.unwrap_or(0.0);
}
// if the node contains a width or height attribute it overrides the other size
for a in node_view.attributes().into_iter().flatten() {
match &*a.attribute.name {
"width" => width = a.value.as_float().unwrap(),
"height" => height = a.value.as_float().unwrap(),
// because Size only depends on the width and height, no other attributes will be passed to the member
_ => panic!(),
}
}
// to determine what other parts of the dom need to be updated we return a boolean that marks if this member changed
let changed = (width != self.0) || (height != self.1);
*self = Self(width, height);
changed
}
}
#[derive(Debug, Clone, Copy, PartialEq, Default, Component)]
struct TextColor {
r: u8,
g: u8,
b: u8,
}
#[partial_derive_state]
impl State for TextColor {
// TextColor depends on the TextColor part of the parent
type ParentDependencies = (Self,);
type ChildDependencies = ();
type NodeDependencies = ();
// TextColor only cares about the color attribute of the current node
const NODE_MASK: NodeMaskBuilder<'static> =
// Get access to the color attribute
NodeMaskBuilder::new().with_attrs(AttributeMaskBuilder::Some(&["color"]));
fn update<'a>(
&mut self,
node_view: NodeView<()>,
_node: <Self::NodeDependencies as Dependancy>::ElementBorrowed<'a>,
parent: Option<<Self::ParentDependencies as Dependancy>::ElementBorrowed<'a>>,
_children: Vec<<Self::ChildDependencies as Dependancy>::ElementBorrowed<'a>>,
_context: &SendAnyMap,
) -> bool {
// TextColor only depends on the color tag, so getting the first tag is equivilent to looking through all tags
let new = match node_view
.attributes()
.and_then(|mut attrs| attrs.next())
.and_then(|attr| attr.value.as_text())
{
// if there is a color tag, translate it
Some("red") => TextColor { r: 255, g: 0, b: 0 },
Some("green") => TextColor { r: 0, g: 255, b: 0 },
Some("blue") => TextColor { r: 0, g: 0, b: 255 },
Some(color) => panic!("unknown color {color}"),
// otherwise check if the node has a parent and inherit that color
None => match parent {
Some((parent,)) => *parent,
None => Self::default(),
},
};
// check if the member has changed
let changed = new != *self;
*self = new;
changed
}
}
#[derive(Debug, Clone, Copy, PartialEq, Default, Component)]
struct Border(bool);
#[partial_derive_state]
impl State for Border {
// TextColor depends on the TextColor part of the parent
type ParentDependencies = (Self,);
type ChildDependencies = ();
type NodeDependencies = ();
// Border does not depended on any other member in the current node
const NODE_MASK: NodeMaskBuilder<'static> =
// Get access to the border attribute
NodeMaskBuilder::new().with_attrs(AttributeMaskBuilder::Some(&["border"]));
fn update<'a>(
&mut self,
node_view: NodeView<()>,
_node: <Self::NodeDependencies as Dependancy>::ElementBorrowed<'a>,
_parent: Option<<Self::ParentDependencies as Dependancy>::ElementBorrowed<'a>>,
_children: Vec<<Self::ChildDependencies as Dependancy>::ElementBorrowed<'a>>,
_context: &SendAnyMap,
) -> bool {
// check if the node contians a border attribute
let new = Self(
node_view
.attributes()
.and_then(|mut attrs| attrs.next().map(|a| a.attribute.name == "border"))
.is_some(),
);
// check if the member has changed
let changed = new != *self;
*self = new;
changed
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
fn app(cx: Scope) -> Element {
let count = use_state(cx, || 0);
use_future(cx, (count,), |(count,)| async move {
loop {
tokio::time::sleep(std::time::Duration::from_millis(2)).await;
if *count.get() == 50 {
count.set(0);
} else {
count.set(*count + 1);
}
}
});
cx.render(rsx! {
div { color: "red",
"{count}"
div {
for el in 0..*count.get() {
rsx!(
Comp {
key: "{el}",
}
)
}
}
div {
for el in 0..*count.get() {
rsx!(
Comp {
key: "{el}",
}
)
}
}
}
})
}
fn Comp(cx: Scope) -> Element {
cx.render(rsx! { div { border: "", "hello world" } })
}
// create the vdom, the real_dom, and the binding layer between them
let mut vdom = VirtualDom::new(app);
let mut rdom: RealDom = RealDom::new([
Border::to_type_erased(),
TextColor::to_type_erased(),
Size::to_type_erased(),
]);
let mut dioxus_intigration_state = DioxusState::create(&mut rdom);
let mutations = vdom.rebuild();
// update the structure of the real_dom tree
dioxus_intigration_state.apply_mutations(&mut rdom, mutations);
let mut ctx = SendAnyMap::new();
// set the font size to 3.3
ctx.insert(FontSize(3.3));
// update the State for nodes in the real_dom tree
let _to_rerender = rdom.update_state(ctx);
// we need to run the vdom in a async runtime
tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()?
.block_on(async {
loop {
// wait for the vdom to update
vdom.wait_for_work().await;
// get the mutations from the vdom
let mutations = vdom.render_immediate();
// update the structure of the real_dom tree
dioxus_intigration_state.apply_mutations(&mut rdom, mutations);
// update the state of the real_dom tree
let mut ctx = SendAnyMap::new();
// set the font size to 3.3
ctx.insert(FontSize(3.3));
let _to_rerender = rdom.update_state(ctx);
println!("DOM size: {}", rdom.tree_ref().len());
}
})
}