Skip to content

Commit 9d50f90

Browse files
committed
Fix rendering bugs (yewstack#1225)
1 parent fc1861a commit 9d50f90

File tree

2 files changed

+125
-19
lines changed

2 files changed

+125
-19
lines changed

yew/src/html/scope.rs

Lines changed: 120 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -120,13 +120,17 @@ impl<COMP: Component> Scope<COMP> {
120120
node_ref: NodeRef,
121121
props: COMP::Properties,
122122
) -> Scope<COMP> {
123-
*self.state.borrow_mut() = Some(ComponentState::new(
124-
element,
125-
ancestor,
126-
node_ref,
127-
self.clone(),
128-
props,
129-
));
123+
scheduler().push_comp(
124+
ComponentRunnableType::Create,
125+
Box::new(CreateComponent {
126+
state: self.state.clone(),
127+
element,
128+
ancestor,
129+
node_ref,
130+
scope: self.clone(),
131+
props,
132+
}),
133+
);
130134
self.update(ComponentUpdate::Force, true);
131135
self
132136
}
@@ -216,13 +220,16 @@ impl<COMP: Component> Scope<COMP> {
216220
}
217221
}
218222

223+
type Dirty = bool;
224+
const DIRTY: Dirty = true;
225+
219226
struct ComponentState<COMP: Component> {
220227
element: Element,
221228
node_ref: NodeRef,
222229
scope: Scope<COMP>,
223230
component: Box<COMP>,
224231
last_root: Option<VNode>,
225-
rendered: bool,
232+
render_status: Option<Dirty>,
226233
}
227234

228235
impl<COMP: Component> ComponentState<COMP> {
@@ -240,7 +247,37 @@ impl<COMP: Component> ComponentState<COMP> {
240247
scope,
241248
component,
242249
last_root: ancestor,
243-
rendered: false,
250+
render_status: None,
251+
}
252+
}
253+
}
254+
255+
struct CreateComponent<COMP>
256+
where
257+
COMP: Component,
258+
{
259+
state: Shared<Option<ComponentState<COMP>>>,
260+
element: Element,
261+
ancestor: Option<VNode>,
262+
node_ref: NodeRef,
263+
scope: Scope<COMP>,
264+
props: COMP::Properties,
265+
}
266+
267+
impl<COMP> Runnable for CreateComponent<COMP>
268+
where
269+
COMP: Component,
270+
{
271+
fn run(self: Box<Self>) {
272+
let mut current_state = self.state.borrow_mut();
273+
if current_state.is_none() {
274+
*current_state = Some(ComponentState::new(
275+
self.element,
276+
self.ancestor,
277+
self.node_ref,
278+
self.scope,
279+
self.props,
280+
));
244281
}
245282
}
246283
}
@@ -274,7 +311,7 @@ where
274311
};
275312

276313
if should_update {
277-
state.rendered = false;
314+
state.render_status = state.render_status.map(|_| DIRTY);
278315
let mut root = state.component.render();
279316
let last_root = state.last_root.take();
280317
if let Some(node) =
@@ -308,10 +345,16 @@ where
308345
{
309346
fn run(self: Box<Self>) {
310347
if let Some(mut state) = self.state.borrow_mut().as_mut() {
311-
if !state.rendered {
312-
state.rendered = true;
313-
state.component.rendered(self.first_render);
348+
if self.first_render && state.render_status.is_some() {
349+
return;
314350
}
351+
352+
if !self.first_render && state.render_status != Some(DIRTY) {
353+
return;
354+
}
355+
356+
state.render_status = Some(!DIRTY);
357+
state.component.rendered(self.first_render);
315358
}
316359
}
317360
}
@@ -351,17 +394,22 @@ mod tests {
351394
#[derive(Clone, Properties)]
352395
struct Props {
353396
lifecycle: Rc<RefCell<Vec<String>>>,
397+
create_message: Option<bool>,
354398
}
399+
355400
struct Comp {
356401
props: Props,
357402
}
358403

359404
impl Component for Comp {
360-
type Message = ();
405+
type Message = bool;
361406
type Properties = Props;
362407

363-
fn create(props: Self::Properties, _: ComponentLink<Self>) -> Self {
408+
fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
364409
props.lifecycle.borrow_mut().push("create".into());
410+
if let Some(msg) = props.create_message {
411+
link.send_message(msg);
412+
}
365413
Comp { props }
366414
}
367415

@@ -372,9 +420,12 @@ mod tests {
372420
.push(format!("rendered({})", first_render));
373421
}
374422

375-
fn update(&mut self, _: Self::Message) -> ShouldRender {
376-
self.props.lifecycle.borrow_mut().push("update".into());
377-
false
423+
fn update(&mut self, msg: Self::Message) -> ShouldRender {
424+
self.props
425+
.lifecycle
426+
.borrow_mut()
427+
.push(format!("update({})", msg));
428+
msg
378429
}
379430

380431
fn change(&mut self, _: Self::Properties) -> ShouldRender {
@@ -395,11 +446,12 @@ mod tests {
395446
}
396447

397448
#[test]
398-
fn text_mount_in_place() {
449+
fn mount() {
399450
let document = crate::utils::document();
400451
let lifecycle: Rc<RefCell<Vec<String>>> = Rc::default();
401452
let props = Props {
402453
lifecycle: lifecycle.clone(),
454+
create_message: None,
403455
};
404456

405457
let scope = Scope::<Comp>::new(None);
@@ -415,4 +467,53 @@ mod tests {
415467
]
416468
);
417469
}
470+
471+
#[test]
472+
fn mount_with_create_message() {
473+
let document = crate::utils::document();
474+
let lifecycle: Rc<RefCell<Vec<String>>> = Rc::default();
475+
let props = Props {
476+
lifecycle: lifecycle.clone(),
477+
create_message: Some(false),
478+
};
479+
480+
let scope = Scope::<Comp>::new(None);
481+
let el = document.create_element("div").unwrap();
482+
scope.mount_in_place(el, None, NodeRef::default(), props);
483+
484+
assert_eq!(
485+
lifecycle.borrow_mut().deref(),
486+
&vec![
487+
"create".to_string(),
488+
"update(false)".to_string(),
489+
"view".to_string(),
490+
"rendered(true)".to_string()
491+
]
492+
);
493+
}
494+
495+
#[test]
496+
fn mount_with_create_render_message() {
497+
let document = crate::utils::document();
498+
let lifecycle: Rc<RefCell<Vec<String>>> = Rc::default();
499+
let props = Props {
500+
lifecycle: lifecycle.clone(),
501+
create_message: Some(true),
502+
};
503+
504+
let scope = Scope::<Comp>::new(None);
505+
let el = document.create_element("div").unwrap();
506+
scope.mount_in_place(el, None, NodeRef::default(), props);
507+
508+
assert_eq!(
509+
lifecycle.borrow_mut().deref(),
510+
&vec![
511+
"create".to_string(),
512+
"update(true)".to_string(),
513+
"view".to_string(),
514+
"view".to_string(),
515+
"rendered(true)".to_string()
516+
]
517+
);
518+
}
418519
}

yew/src/scheduler.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ pub(crate) struct Scheduler {
3131

3232
pub(crate) enum ComponentRunnableType {
3333
Destroy,
34+
Create,
3435
Update,
3536
Rendered,
3637
}
@@ -39,6 +40,7 @@ pub(crate) enum ComponentRunnableType {
3940
struct ComponentScheduler {
4041
// Queues
4142
destroy: Shared<VecDeque<Box<dyn Runnable>>>,
43+
create: Shared<VecDeque<Box<dyn Runnable>>>,
4244
update: Shared<VecDeque<Box<dyn Runnable>>>,
4345

4446
// Stack
@@ -49,13 +51,15 @@ impl ComponentScheduler {
4951
fn new() -> Self {
5052
ComponentScheduler {
5153
destroy: Rc::new(RefCell::new(VecDeque::new())),
54+
create: Rc::new(RefCell::new(VecDeque::new())),
5255
update: Rc::new(RefCell::new(VecDeque::new())),
5356
rendered: Rc::new(RefCell::new(Vec::new())),
5457
}
5558
}
5659

5760
fn next_runnable(&self) -> Option<Box<dyn Runnable>> {
5861
None.or_else(|| self.destroy.borrow_mut().pop_front())
62+
.or_else(|| self.create.borrow_mut().pop_front())
5963
.or_else(|| self.update.borrow_mut().pop_front())
6064
.or_else(|| self.rendered.borrow_mut().pop())
6165
}
@@ -75,6 +79,7 @@ impl Scheduler {
7579
ComponentRunnableType::Destroy => {
7680
self.component.destroy.borrow_mut().push_back(runnable)
7781
}
82+
ComponentRunnableType::Create => self.component.create.borrow_mut().push_back(runnable),
7883
ComponentRunnableType::Update => self.component.update.borrow_mut().push_back(runnable),
7984
ComponentRunnableType::Rendered => self.component.rendered.borrow_mut().push(runnable),
8085
};

0 commit comments

Comments
 (0)