Skip to content

Commit 25b42f2

Browse files
authored
Render after each component update (#1373)
1 parent 35e9e86 commit 25b42f2

File tree

3 files changed

+73
-26
lines changed

3 files changed

+73
-26
lines changed

yew/src/html/scope.rs

Lines changed: 68 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ cfg_if! {
1717

1818
/// Updates for a `Component` instance. Used by scope sender.
1919
pub(crate) enum ComponentUpdate<COMP: Component> {
20-
/// Force update
21-
Force,
20+
/// First update
21+
First,
2222
/// Wraps messages for a component.
2323
Message(COMP::Message),
2424
/// Wraps batch of messages for a component.
@@ -159,7 +159,10 @@ impl<COMP: Component> Scope<COMP> {
159159
node_ref: NodeRef,
160160
props: COMP::Properties,
161161
) -> Scope<COMP> {
162-
scheduler().push_comp(
162+
let scheduler = scheduler();
163+
// Hold scheduler lock so that `create` doesn't run until `update` is scheduled
164+
let lock = scheduler.lock();
165+
scheduler.push_comp(
163166
ComponentRunnableType::Create,
164167
Box::new(CreateComponent {
165168
state: self.state.clone(),
@@ -171,28 +174,19 @@ impl<COMP: Component> Scope<COMP> {
171174
props,
172175
}),
173176
);
174-
self.update(ComponentUpdate::Force, true);
177+
self.update(ComponentUpdate::First);
178+
drop(lock);
179+
scheduler.start();
175180
self
176181
}
177182

178183
/// Schedules a task to send an update to a component
179-
pub(crate) fn update(&self, update: ComponentUpdate<COMP>, first_update: bool) {
184+
pub(crate) fn update(&self, update: ComponentUpdate<COMP>) {
180185
let update = UpdateComponent {
181186
state: self.state.clone(),
182187
update,
183188
};
184189
scheduler().push_comp(ComponentRunnableType::Update, Box::new(update));
185-
self.render(first_update);
186-
}
187-
188-
/// Schedules a task to render the component and call its rendered method
189-
pub(crate) fn render(&self, first_render: bool) {
190-
let state = self.state.clone();
191-
let rendered = RenderComponent {
192-
state,
193-
first_render,
194-
};
195-
scheduler().push_comp(ComponentRunnableType::Render, Box::new(rendered));
196190
}
197191

198192
/// Send a message to the component.
@@ -203,7 +197,7 @@ impl<COMP: Component> Scope<COMP> {
203197
where
204198
T: Into<COMP::Message>,
205199
{
206-
self.update(ComponentUpdate::Message(msg.into()), false);
200+
self.update(ComponentUpdate::Message(msg.into()));
207201
}
208202

209203
/// Send a batch of messages to the component.
@@ -215,7 +209,7 @@ impl<COMP: Component> Scope<COMP> {
215209
/// Please be aware that currently this method synchronously
216210
/// schedules calls to the [Component](Component) interface.
217211
pub fn send_message_batch(&self, messages: Vec<COMP::Message>) {
218-
self.update(ComponentUpdate::MessageBatch(messages), false);
212+
self.update(ComponentUpdate::MessageBatch(messages));
219213
}
220214

221215
/// Creates a `Callback` which will send a message to the linked
@@ -285,6 +279,7 @@ struct ComponentState<COMP: Component> {
285279
last_root: Option<VNode>,
286280
new_root: Option<VNode>,
287281
has_rendered: bool,
282+
pending_updates: Vec<Box<UpdateComponent<COMP>>>,
288283
}
289284

290285
impl<COMP: Component> ComponentState<COMP> {
@@ -309,6 +304,7 @@ impl<COMP: Component> ComponentState<COMP> {
309304
last_root: None,
310305
new_root: None,
311306
has_rendered: false,
307+
pending_updates: Vec::new(),
312308
}
313309
}
314310
}
@@ -362,9 +358,20 @@ where
362358
COMP: Component,
363359
{
364360
fn run(self: Box<Self>) {
365-
if let Some(mut state) = self.state.borrow_mut().as_mut() {
361+
let state_clone = self.state.clone();
362+
if let Some(mut state) = state_clone.borrow_mut().as_mut() {
363+
if state.new_root.is_some() {
364+
state.pending_updates.push(self);
365+
return;
366+
}
367+
368+
let first_update = match self.update {
369+
ComponentUpdate::First => true,
370+
_ => false,
371+
};
372+
366373
let should_update = match self.update {
367-
ComponentUpdate::Force => true,
374+
ComponentUpdate::First => true,
368375
ComponentUpdate::Message(message) => state.component.update(message),
369376
ComponentUpdate::MessageBatch(messages) => messages
370377
.into_iter()
@@ -378,8 +385,15 @@ where
378385

379386
if should_update {
380387
state.new_root = Some(state.component.render());
388+
scheduler().push_comp(
389+
ComponentRunnableType::Render,
390+
Box::new(RenderComponent {
391+
state: self.state,
392+
first_render: first_update,
393+
}),
394+
);
381395
};
382-
}
396+
};
383397
}
384398
}
385399

@@ -445,6 +459,9 @@ where
445459

446460
state.has_rendered = true;
447461
state.component.rendered(self.first_render);
462+
for update in state.pending_updates.drain(..) {
463+
scheduler().push_comp(ComponentRunnableType::Update, update);
464+
}
448465
}
449466
}
450467
}
@@ -523,6 +540,7 @@ mod tests {
523540
struct Props {
524541
lifecycle: Rc<RefCell<Vec<String>>>,
525542
create_message: Option<bool>,
543+
update_message: RefCell<Option<bool>>,
526544
view_message: RefCell<Option<bool>>,
527545
rendered_message: RefCell<Option<bool>>,
528546
}
@@ -555,6 +573,9 @@ mod tests {
555573
}
556574

557575
fn update(&mut self, msg: Self::Message) -> ShouldRender {
576+
if let Some(msg) = self.props.update_message.borrow_mut().take() {
577+
self.link.send_message(msg);
578+
}
558579
self.props
559580
.lifecycle
560581
.borrow_mut()
@@ -619,10 +640,10 @@ mod tests {
619640
},
620641
&vec![
621642
"create".to_string(),
622-
"update(false)".to_string(),
623643
"view".to_string(),
624644
"child rendered".to_string(),
625645
"rendered(true)".to_string(),
646+
"update(false)".to_string(),
626647
],
627648
);
628649

@@ -635,10 +656,11 @@ mod tests {
635656
&vec![
636657
"create".to_string(),
637658
"view".to_string(),
638-
"update(true)".to_string(),
639-
"view".to_string(),
640659
"child rendered".to_string(),
641660
"rendered(true)".to_string(),
661+
"update(true)".to_string(),
662+
"view".to_string(),
663+
"rendered(false)".to_string(),
642664
],
643665
);
644666

@@ -651,9 +673,9 @@ mod tests {
651673
&vec![
652674
"create".to_string(),
653675
"view".to_string(),
654-
"update(false)".to_string(),
655676
"child rendered".to_string(),
656677
"rendered(true)".to_string(),
678+
"update(false)".to_string(),
657679
],
658680
);
659681

@@ -688,5 +710,26 @@ mod tests {
688710
"rendered(false)".to_string(),
689711
],
690712
);
713+
714+
test_lifecycle(
715+
Props {
716+
lifecycle: lifecycle.clone(),
717+
create_message: Some(true),
718+
update_message: RefCell::new(Some(true)),
719+
..Props::default()
720+
},
721+
&vec![
722+
"create".to_string(),
723+
"view".to_string(),
724+
"child rendered".to_string(),
725+
"rendered(true)".to_string(),
726+
"update(true)".to_string(),
727+
"view".to_string(),
728+
"rendered(false)".to_string(),
729+
"update(true)".to_string(),
730+
"view".to_string(),
731+
"rendered(false)".to_string(),
732+
],
733+
);
691734
}
692735
}

yew/src/scheduler.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,10 @@ impl Scheduler {
9797
self.start();
9898
}
9999

100+
pub(crate) fn lock(&self) -> Option<std::cell::Ref<'_, ()>> {
101+
self.lock.try_borrow().ok()
102+
}
103+
100104
fn next_runnable(&self) -> Option<Box<dyn Runnable>> {
101105
None.or_else(|| self.component.next_runnable())
102106
.or_else(|| self.main.borrow_mut().pop_front())

yew/src/virtual_dom/vcomp.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ impl<COMP: Component> Mountable for PropsWrapper<COMP> {
164164

165165
fn reuse(self: Box<Self>, scope: &dyn Scoped, next_sibling: NodeRef) {
166166
let scope: Scope<COMP> = scope.to_any().downcast();
167-
scope.update(ComponentUpdate::Properties(self.props, next_sibling), false);
167+
scope.update(ComponentUpdate::Properties(self.props, next_sibling));
168168
}
169169
}
170170

0 commit comments

Comments
 (0)