Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Render after each component update #1373

Merged
merged 1 commit into from
Jul 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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