Skip to content

Commit

Permalink
Render after each component update (#1373)
Browse files Browse the repository at this point in the history
  • Loading branch information
jstarry authored Jul 4, 2020
1 parent 35e9e86 commit 25b42f2
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 26 deletions.
93 changes: 68 additions & 25 deletions yew/src/html/scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ cfg_if! {

/// Updates for a `Component` instance. Used by scope sender.
pub(crate) enum ComponentUpdate<COMP: Component> {
/// Force update
Force,
/// First update
First,
/// Wraps messages for a component.
Message(COMP::Message),
/// Wraps batch of messages for a component.
Expand Down Expand Up @@ -159,7 +159,10 @@ impl<COMP: Component> Scope<COMP> {
node_ref: NodeRef,
props: COMP::Properties,
) -> Scope<COMP> {
scheduler().push_comp(
let scheduler = scheduler();
// Hold scheduler lock so that `create` doesn't run until `update` is scheduled
let lock = scheduler.lock();
scheduler.push_comp(
ComponentRunnableType::Create,
Box::new(CreateComponent {
state: self.state.clone(),
Expand All @@ -171,28 +174,19 @@ impl<COMP: Component> Scope<COMP> {
props,
}),
);
self.update(ComponentUpdate::Force, true);
self.update(ComponentUpdate::First);
drop(lock);
scheduler.start();
self
}

/// Schedules a task to send an update to a component
pub(crate) fn update(&self, update: ComponentUpdate<COMP>, first_update: bool) {
pub(crate) fn update(&self, update: ComponentUpdate<COMP>) {
let update = UpdateComponent {
state: self.state.clone(),
update,
};
scheduler().push_comp(ComponentRunnableType::Update, Box::new(update));
self.render(first_update);
}

/// Schedules a task to render the component and call its rendered method
pub(crate) fn render(&self, first_render: bool) {
let state = self.state.clone();
let rendered = RenderComponent {
state,
first_render,
};
scheduler().push_comp(ComponentRunnableType::Render, Box::new(rendered));
}

/// Send a message to the component.
Expand All @@ -203,7 +197,7 @@ impl<COMP: Component> Scope<COMP> {
where
T: Into<COMP::Message>,
{
self.update(ComponentUpdate::Message(msg.into()), false);
self.update(ComponentUpdate::Message(msg.into()));
}

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

/// Creates a `Callback` which will send a message to the linked
Expand Down Expand Up @@ -285,6 +279,7 @@ struct ComponentState<COMP: Component> {
last_root: Option<VNode>,
new_root: Option<VNode>,
has_rendered: bool,
pending_updates: Vec<Box<UpdateComponent<COMP>>>,
}

impl<COMP: Component> ComponentState<COMP> {
Expand All @@ -309,6 +304,7 @@ impl<COMP: Component> ComponentState<COMP> {
last_root: None,
new_root: None,
has_rendered: false,
pending_updates: Vec::new(),
}
}
}
Expand Down Expand Up @@ -362,9 +358,20 @@ where
COMP: Component,
{
fn run(self: Box<Self>) {
if let Some(mut state) = self.state.borrow_mut().as_mut() {
let state_clone = self.state.clone();
if let Some(mut state) = state_clone.borrow_mut().as_mut() {
if state.new_root.is_some() {
state.pending_updates.push(self);
return;
}

let first_update = match self.update {
ComponentUpdate::First => true,
_ => false,
};

let should_update = match self.update {
ComponentUpdate::Force => true,
ComponentUpdate::First => true,
ComponentUpdate::Message(message) => state.component.update(message),
ComponentUpdate::MessageBatch(messages) => messages
.into_iter()
Expand All @@ -378,8 +385,15 @@ where

if should_update {
state.new_root = Some(state.component.render());
scheduler().push_comp(
ComponentRunnableType::Render,
Box::new(RenderComponent {
state: self.state,
first_render: first_update,
}),
);
};
}
};
}
}

Expand Down Expand Up @@ -445,6 +459,9 @@ where

state.has_rendered = true;
state.component.rendered(self.first_render);
for update in state.pending_updates.drain(..) {
scheduler().push_comp(ComponentRunnableType::Update, update);
}
}
}
}
Expand Down Expand Up @@ -523,6 +540,7 @@ mod tests {
struct Props {
lifecycle: Rc<RefCell<Vec<String>>>,
create_message: Option<bool>,
update_message: RefCell<Option<bool>>,
view_message: RefCell<Option<bool>>,
rendered_message: RefCell<Option<bool>>,
}
Expand Down Expand Up @@ -555,6 +573,9 @@ mod tests {
}

fn update(&mut self, msg: Self::Message) -> ShouldRender {
if let Some(msg) = self.props.update_message.borrow_mut().take() {
self.link.send_message(msg);
}
self.props
.lifecycle
.borrow_mut()
Expand Down Expand Up @@ -619,10 +640,10 @@ mod tests {
},
&vec![
"create".to_string(),
"update(false)".to_string(),
"view".to_string(),
"child rendered".to_string(),
"rendered(true)".to_string(),
"update(false)".to_string(),
],
);

Expand All @@ -635,10 +656,11 @@ mod tests {
&vec![
"create".to_string(),
"view".to_string(),
"update(true)".to_string(),
"view".to_string(),
"child rendered".to_string(),
"rendered(true)".to_string(),
"update(true)".to_string(),
"view".to_string(),
"rendered(false)".to_string(),
],
);

Expand All @@ -651,9 +673,9 @@ mod tests {
&vec![
"create".to_string(),
"view".to_string(),
"update(false)".to_string(),
"child rendered".to_string(),
"rendered(true)".to_string(),
"update(false)".to_string(),
],
);

Expand Down Expand Up @@ -688,5 +710,26 @@ mod tests {
"rendered(false)".to_string(),
],
);

test_lifecycle(
Props {
lifecycle: lifecycle.clone(),
create_message: Some(true),
update_message: RefCell::new(Some(true)),
..Props::default()
},
&vec![
"create".to_string(),
"view".to_string(),
"child rendered".to_string(),
"rendered(true)".to_string(),
"update(true)".to_string(),
"view".to_string(),
"rendered(false)".to_string(),
"update(true)".to_string(),
"view".to_string(),
"rendered(false)".to_string(),
],
);
}
}
4 changes: 4 additions & 0 deletions yew/src/scheduler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ impl Scheduler {
self.start();
}

pub(crate) fn lock(&self) -> Option<std::cell::Ref<'_, ()>> {
self.lock.try_borrow().ok()
}

fn next_runnable(&self) -> Option<Box<dyn Runnable>> {
None.or_else(|| self.component.next_runnable())
.or_else(|| self.main.borrow_mut().pop_front())
Expand Down
2 changes: 1 addition & 1 deletion yew/src/virtual_dom/vcomp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ impl<COMP: Component> Mountable for PropsWrapper<COMP> {

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

Expand Down

0 comments on commit 25b42f2

Please sign in to comment.