Skip to content

Commit

Permalink
Fix children re-render component bug (#980)
Browse files Browse the repository at this point in the history
* Fix children re-render component bug

* cargo clippy
  • Loading branch information
jstarry authored Feb 29, 2020
1 parent c02609a commit f659052
Showing 1 changed file with 72 additions and 62 deletions.
134 changes: 72 additions & 62 deletions src/virtual_dom/vcomp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ use crate::html::{Component, ComponentUpdate, HiddenScope, NodeRef, Scope};
use crate::utils::document;
use cfg_if::cfg_if;
use std::any::TypeId;
use std::cell::RefCell;
use std::fmt;
use std::mem::swap;
use std::rc::Rc;
cfg_if! {
if #[cfg(feature = "std_web")] {
Expand All @@ -29,7 +29,7 @@ enum GeneratorType {
#[derive(Clone)]
pub struct VComp {
type_id: TypeId,
state: Rc<RefCell<MountState>>,
state: MountState,
pub(crate) node_ref: NodeRef,
}

Expand Down Expand Up @@ -69,6 +69,7 @@ where
}
}

#[derive(Clone)]
enum MountState {
Unmounted(Unmounted),
Mounted(Mounted),
Expand All @@ -77,8 +78,9 @@ enum MountState {
Overwritten,
}

#[derive(Clone)]
struct Unmounted {
generator: Box<Generator>,
generator: Rc<Generator>,
}

struct Mounted {
Expand All @@ -87,6 +89,12 @@ struct Mounted {
destroyer: Box<dyn FnOnce()>,
}

impl Clone for Mounted {
fn clone(&self) -> Self {
panic!("Mounted components are not allowed to be cloned!")
}
}

impl VComp {
/// This method prepares a generator to make a new instance of the `Component`.
pub fn new<COMP>(props: COMP::Properties, node_ref: NodeRef) -> Self
Expand Down Expand Up @@ -127,9 +135,9 @@ impl VComp {

VComp {
type_id: TypeId::of::<COMP>(),
state: Rc::new(RefCell::new(MountState::Unmounted(Unmounted {
generator: Box::new(generator),
}))),
state: MountState::Unmounted(Unmounted {
generator: Rc::new(generator),
}),
node_ref,
}
}
Expand All @@ -154,7 +162,9 @@ enum Reform {

impl VDiff for VComp {
fn detach(&mut self, parent: &Element) -> Option<Node> {
match self.state.replace(MountState::Detached) {
let mut replace_state = MountState::Detached;
swap(&mut replace_state, &mut self.state);
match replace_state {
MountState::Mounted(this) => {
(this.destroyer)();
this.node_ref.get().and_then(|node| {
Expand All @@ -175,67 +185,67 @@ impl VDiff for VComp {
previous_sibling: Option<&Node>,
ancestor: Option<VNode>,
) -> Option<Node> {
match self.state.replace(MountState::Mounting) {
MountState::Unmounted(this) => {
let reform = match ancestor {
Some(VNode::VComp(mut vcomp)) => {
// If the ancestor is a Component of the same type, don't replace, keep the
// old Component but update the properties.
if self.type_id == vcomp.type_id {
match vcomp.state.replace(MountState::Overwritten) {
MountState::Mounted(mounted) => Reform::Keep(mounted),
_ => Reform::Before(None),
}
} else {
Reform::Before(vcomp.detach(parent))
let mut replace_state = MountState::Mounting;
swap(&mut replace_state, &mut self.state);
if let MountState::Unmounted(this) = replace_state {
let reform = match ancestor {
Some(VNode::VComp(mut vcomp)) => {
// If the ancestor is a Component of the same type, don't replace, keep the
// old Component but update the properties.
if self.type_id == vcomp.type_id {
let mut replace_state = MountState::Overwritten;
swap(&mut replace_state, &mut vcomp.state);
match replace_state {
MountState::Mounted(mounted) => Reform::Keep(mounted),
_ => Reform::Before(None),
}
} else {
Reform::Before(vcomp.detach(parent))
}
Some(mut vnode) => Reform::Before(vnode.detach(parent)),
None => Reform::Before(None),
};

let mounted = match reform {
Reform::Keep(mounted) => {
// Send properties update when the component is already rendered.
this.replace(mounted)
}
Reform::Before(next_sibling) => {
let dummy_node = document().create_text_node("");
if let Some(next_sibling) = next_sibling {
let next_sibling = &next_sibling;
#[cfg(feature = "web_sys")]
let next_sibling = Some(next_sibling);
parent
.insert_before(&dummy_node, next_sibling)
.expect("can't insert dummy component node before next sibling");
} else if let Some(next_sibling) =
previous_sibling.and_then(|p| p.next_sibling())
}
Some(mut vnode) => Reform::Before(vnode.detach(parent)),
None => Reform::Before(None),
};

let mounted = match reform {
Reform::Keep(mounted) => {
// Send properties update when the component is already rendered.
this.replace(mounted)
}
Reform::Before(next_sibling) => {
let dummy_node = document().create_text_node("");
if let Some(next_sibling) = next_sibling {
let next_sibling = &next_sibling;
#[cfg(feature = "web_sys")]
let next_sibling = Some(next_sibling);
parent
.insert_before(&dummy_node, next_sibling)
.expect("can't insert dummy component node before next sibling");
} else if let Some(next_sibling) =
previous_sibling.and_then(|p| p.next_sibling())
{
let next_sibling = &next_sibling;
#[cfg(feature = "web_sys")]
let next_sibling = Some(next_sibling);
parent
.insert_before(&dummy_node, next_sibling)
.expect("can't insert dummy component node before next sibling");
} else {
#[cfg_attr(
feature = "std_web",
allow(clippy::let_unit_value, unused_variables)
)]
{
let next_sibling = &next_sibling;
let result = parent.append_child(&dummy_node);
#[cfg(feature = "web_sys")]
let next_sibling = Some(next_sibling);
parent
.insert_before(&dummy_node, next_sibling)
.expect("can't insert dummy component node before next sibling");
} else {
#[cfg_attr(
feature = "std_web",
allow(clippy::let_unit_value, unused_variables)
)]
{
let result = parent.append_child(&dummy_node);
#[cfg(feature = "web_sys")]
result.expect("can't append node to parent");
}
result.expect("can't append node to parent");
}
this.mount(parent.to_owned(), dummy_node)
}
};
self.state.replace(MountState::Mounted(mounted));
}
state => {
self.state.replace(state);
}
this.mount(parent.to_owned(), dummy_node)
}
};

self.state = MountState::Mounted(mounted);
}
None
}
Expand Down

0 comments on commit f659052

Please sign in to comment.