Skip to content

Commit

Permalink
Fix rendering bugs (#1225)
Browse files Browse the repository at this point in the history
  • Loading branch information
jstarry authored and mergify[bot] committed May 14, 2020
1 parent fc1861a commit d1fd518
Show file tree
Hide file tree
Showing 2 changed files with 125 additions and 19 deletions.
139 changes: 120 additions & 19 deletions yew/src/html/scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,13 +120,17 @@ impl<COMP: Component> Scope<COMP> {
node_ref: NodeRef,
props: COMP::Properties,
) -> Scope<COMP> {
*self.state.borrow_mut() = Some(ComponentState::new(
element,
ancestor,
node_ref,
self.clone(),
props,
));
scheduler().push_comp(
ComponentRunnableType::Create,
Box::new(CreateComponent {
state: self.state.clone(),
element,
ancestor,
node_ref,
scope: self.clone(),
props,
}),
);
self.update(ComponentUpdate::Force, true);
self
}
Expand Down Expand Up @@ -216,13 +220,16 @@ impl<COMP: Component> Scope<COMP> {
}
}

type Dirty = bool;
const DIRTY: Dirty = true;

struct ComponentState<COMP: Component> {
element: Element,
node_ref: NodeRef,
scope: Scope<COMP>,
component: Box<COMP>,
last_root: Option<VNode>,
rendered: bool,
render_status: Option<Dirty>,
}

impl<COMP: Component> ComponentState<COMP> {
Expand All @@ -240,7 +247,37 @@ impl<COMP: Component> ComponentState<COMP> {
scope,
component,
last_root: ancestor,
rendered: false,
render_status: None,
}
}
}

struct CreateComponent<COMP>
where
COMP: Component,
{
state: Shared<Option<ComponentState<COMP>>>,
element: Element,
ancestor: Option<VNode>,
node_ref: NodeRef,
scope: Scope<COMP>,
props: COMP::Properties,
}

impl<COMP> Runnable for CreateComponent<COMP>
where
COMP: Component,
{
fn run(self: Box<Self>) {
let mut current_state = self.state.borrow_mut();
if current_state.is_none() {
*current_state = Some(ComponentState::new(
self.element,
self.ancestor,
self.node_ref,
self.scope,
self.props,
));
}
}
}
Expand Down Expand Up @@ -274,7 +311,7 @@ where
};

if should_update {
state.rendered = false;
state.render_status = state.render_status.map(|_| DIRTY);
let mut root = state.component.render();
let last_root = state.last_root.take();
if let Some(node) =
Expand Down Expand Up @@ -308,10 +345,16 @@ where
{
fn run(self: Box<Self>) {
if let Some(mut state) = self.state.borrow_mut().as_mut() {
if !state.rendered {
state.rendered = true;
state.component.rendered(self.first_render);
if self.first_render && state.render_status.is_some() {
return;
}

if !self.first_render && state.render_status != Some(DIRTY) {
return;
}

state.render_status = Some(!DIRTY);
state.component.rendered(self.first_render);
}
}
}
Expand Down Expand Up @@ -351,17 +394,22 @@ mod tests {
#[derive(Clone, Properties)]
struct Props {
lifecycle: Rc<RefCell<Vec<String>>>,
create_message: Option<bool>,
}

struct Comp {
props: Props,
}

impl Component for Comp {
type Message = ();
type Message = bool;
type Properties = Props;

fn create(props: Self::Properties, _: ComponentLink<Self>) -> Self {
fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
props.lifecycle.borrow_mut().push("create".into());
if let Some(msg) = props.create_message {
link.send_message(msg);
}
Comp { props }
}

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

fn update(&mut self, _: Self::Message) -> ShouldRender {
self.props.lifecycle.borrow_mut().push("update".into());
false
fn update(&mut self, msg: Self::Message) -> ShouldRender {
self.props
.lifecycle
.borrow_mut()
.push(format!("update({})", msg));
msg
}

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

#[test]
fn text_mount_in_place() {
fn mount() {
let document = crate::utils::document();
let lifecycle: Rc<RefCell<Vec<String>>> = Rc::default();
let props = Props {
lifecycle: lifecycle.clone(),
create_message: None,
};

let scope = Scope::<Comp>::new(None);
Expand All @@ -415,4 +467,53 @@ mod tests {
]
);
}

#[test]
fn mount_with_create_message() {
let document = crate::utils::document();
let lifecycle: Rc<RefCell<Vec<String>>> = Rc::default();
let props = Props {
lifecycle: lifecycle.clone(),
create_message: Some(false),
};

let scope = Scope::<Comp>::new(None);
let el = document.create_element("div").unwrap();
scope.mount_in_place(el, None, NodeRef::default(), props);

assert_eq!(
lifecycle.borrow_mut().deref(),
&vec![
"create".to_string(),
"update(false)".to_string(),
"view".to_string(),
"rendered(true)".to_string()
]
);
}

#[test]
fn mount_with_create_render_message() {
let document = crate::utils::document();
let lifecycle: Rc<RefCell<Vec<String>>> = Rc::default();
let props = Props {
lifecycle: lifecycle.clone(),
create_message: Some(true),
};

let scope = Scope::<Comp>::new(None);
let el = document.create_element("div").unwrap();
scope.mount_in_place(el, None, NodeRef::default(), props);

assert_eq!(
lifecycle.borrow_mut().deref(),
&vec![
"create".to_string(),
"update(true)".to_string(),
"view".to_string(),
"view".to_string(),
"rendered(true)".to_string()
]
);
}
}
5 changes: 5 additions & 0 deletions yew/src/scheduler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ pub(crate) struct Scheduler {

pub(crate) enum ComponentRunnableType {
Destroy,
Create,
Update,
Rendered,
}
Expand All @@ -39,6 +40,7 @@ pub(crate) enum ComponentRunnableType {
struct ComponentScheduler {
// Queues
destroy: Shared<VecDeque<Box<dyn Runnable>>>,
create: Shared<VecDeque<Box<dyn Runnable>>>,
update: Shared<VecDeque<Box<dyn Runnable>>>,

// Stack
Expand All @@ -49,13 +51,15 @@ impl ComponentScheduler {
fn new() -> Self {
ComponentScheduler {
destroy: Rc::new(RefCell::new(VecDeque::new())),
create: Rc::new(RefCell::new(VecDeque::new())),
update: Rc::new(RefCell::new(VecDeque::new())),
rendered: Rc::new(RefCell::new(Vec::new())),
}
}

fn next_runnable(&self) -> Option<Box<dyn Runnable>> {
None.or_else(|| self.destroy.borrow_mut().pop_front())
.or_else(|| self.create.borrow_mut().pop_front())
.or_else(|| self.update.borrow_mut().pop_front())
.or_else(|| self.rendered.borrow_mut().pop())
}
Expand All @@ -75,6 +79,7 @@ impl Scheduler {
ComponentRunnableType::Destroy => {
self.component.destroy.borrow_mut().push_back(runnable)
}
ComponentRunnableType::Create => self.component.create.borrow_mut().push_back(runnable),
ComponentRunnableType::Update => self.component.update.borrow_mut().push_back(runnable),
ComponentRunnableType::Rendered => self.component.rendered.borrow_mut().push(runnable),
};
Expand Down

0 comments on commit d1fd518

Please sign in to comment.