diff --git a/packages/yew-macro/src/html_tree/html_element.rs b/packages/yew-macro/src/html_tree/html_element.rs
index 3770de65111..24d0b7ac894 100644
--- a/packages/yew-macro/src/html_tree/html_element.rs
+++ b/packages/yew-macro/src/html_tree/html_element.rs
@@ -221,11 +221,13 @@ impl ToTokens for HtmlElement {
quote! { ::std::vec![#(#listeners_it),*].into_iter().flatten().collect() }
};
+ // TODO: if none of the children have possibly None expressions or literals as keys, we can
+ // compute `VList.fully_keyed` at compile time.
let child_list = quote! {
- ::yew::virtual_dom::VList{
- key: ::std::option::Option::None,
- children: #children,
- }
+ ::yew::virtual_dom::VList::with_children(
+ #children,
+ ::std::option::Option::None,
+ )
};
tokens.extend(match &name {
diff --git a/packages/yew-macro/src/html_tree/html_list.rs b/packages/yew-macro/src/html_tree/html_list.rs
index a9a75ce93f0..6a88d317e76 100644
--- a/packages/yew-macro/src/html_tree/html_list.rs
+++ b/packages/yew-macro/src/html_tree/html_list.rs
@@ -77,7 +77,7 @@ impl ToTokens for HtmlList {
tokens.extend(quote_spanned! {spanned.span()=>
::yew::virtual_dom::VNode::VList(
- ::yew::virtual_dom::VList::new_with_children(#children, #key)
+ ::yew::virtual_dom::VList::with_children(#children, #key)
)
});
}
diff --git a/packages/yew/src/html/component/scope.rs b/packages/yew/src/html/component/scope.rs
index 4f4eb692ea7..1916f1a94d1 100644
--- a/packages/yew/src/html/component/scope.rs
+++ b/packages/yew/src/html/component/scope.rs
@@ -172,7 +172,7 @@ impl Scope {
) {
let placeholder = {
let placeholder: Node = document().create_text_node("").into();
- insert_node(&placeholder, &parent, next_sibling.get());
+ insert_node(&placeholder, &parent, next_sibling.get().as_ref());
node_ref.set(Some(placeholder.clone()));
VNode::VRef(placeholder)
};
diff --git a/packages/yew/src/html/mod.rs b/packages/yew/src/html/mod.rs
index 6e761a2f959..efda4d1fb18 100644
--- a/packages/yew/src/html/mod.rs
+++ b/packages/yew/src/html/mod.rs
@@ -66,7 +66,7 @@ pub type Html = VNode;
/// ```
/// ## Relevant examples
/// - [Node Refs](https://github.com/yewstack/yew/tree/master/examples/node_refs)
-#[derive(Debug, Default, Clone)]
+#[derive(Default, Clone)]
pub struct NodeRef(Rc>);
impl PartialEq for NodeRef {
@@ -75,6 +75,16 @@ impl PartialEq for NodeRef {
}
}
+impl std::fmt::Debug for NodeRef {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(
+ f,
+ "NodeRef {{ references: {:?} }}",
+ self.get().map(|n| crate::utils::print_node(&n))
+ )
+ }
+}
+
#[derive(PartialEq, Debug, Default, Clone)]
struct NodeRefInner {
node: Option,
diff --git a/packages/yew/src/utils/mod.rs b/packages/yew/src/utils/mod.rs
index e2788fbaaab..c9c0c52a8d8 100644
--- a/packages/yew/src/utils/mod.rs
+++ b/packages/yew/src/utils/mod.rs
@@ -94,3 +94,13 @@ impl IntoIterator for NodeSeq {
self.0.into_iter()
}
}
+
+/// Print the [web_sys::Node]'s contents as a string for debugging purposes
+pub fn print_node(n: &web_sys::Node) -> String {
+ use wasm_bindgen::JsCast;
+
+ match n.dyn_ref::() {
+ Some(el) => el.outer_html(),
+ None => n.text_content().unwrap_or_default(),
+ }
+}
diff --git a/packages/yew/src/virtual_dom/mod.rs b/packages/yew/src/virtual_dom/mod.rs
index d9c134ed52c..a6ee9d32ee6 100644
--- a/packages/yew/src/virtual_dom/mod.rs
+++ b/packages/yew/src/virtual_dom/mod.rs
@@ -422,10 +422,10 @@ pub(crate) trait VDiff {
) -> NodeRef;
}
-pub(crate) fn insert_node(node: &Node, parent: &Element, next_sibling: Option) {
+pub(crate) fn insert_node(node: &Node, parent: &Element, next_sibling: Option<&Node>) {
match next_sibling {
Some(next_sibling) => parent
- .insert_before(&node, Some(&next_sibling))
+ .insert_before(&node, Some(next_sibling))
.expect("failed to insert tag before next sibling"),
None => parent.append_child(node).expect("failed to append child"),
};
diff --git a/packages/yew/src/virtual_dom/vcomp.rs b/packages/yew/src/virtual_dom/vcomp.rs
index e49f417c462..d74c4cdb46a 100644
--- a/packages/yew/src/virtual_dom/vcomp.rs
+++ b/packages/yew/src/virtual_dom/vcomp.rs
@@ -99,7 +99,6 @@ impl VComp {
}
}
- #[allow(unused)]
pub(crate) fn root_vnode(&self) -> Option + '_> {
self.scope.as_ref().and_then(|scope| scope.root_vnode())
}
@@ -202,7 +201,7 @@ impl PartialEq for VComp {
impl fmt::Debug for VComp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.write_str("VComp")
+ write!(f, "VComp {{ root: {:?} }}", self.root_vnode().as_deref())
}
}
diff --git a/packages/yew/src/virtual_dom/vlist.rs b/packages/yew/src/virtual_dom/vlist.rs
index edaf899d2f1..a146820a832 100644
--- a/packages/yew/src/virtual_dom/vlist.rs
+++ b/packages/yew/src/virtual_dom/vlist.rs
@@ -1,18 +1,32 @@
//! This module contains fragments implementation.
use super::{Key, VDiff, VNode, VText};
use crate::html::{AnyScope, NodeRef};
-use std::collections::{HashMap, HashSet};
+use std::collections::HashMap;
use std::ops::{Deref, DerefMut};
use web_sys::Element;
/// This struct represents a fragment of the Virtual DOM tree.
-#[derive(Clone, Debug, PartialEq, Default)]
+#[derive(Clone, Debug, PartialEq)]
pub struct VList {
- /// The list of children nodes.
- pub children: Vec,
+ /// The list of child [VNode]s
+ children: Vec,
+
+ /// All [VNode]s in the VList have keys
+ fully_keyed: bool,
+
pub key: Option,
}
+impl Default for VList {
+ fn default() -> Self {
+ Self {
+ children: Default::default(),
+ key: None,
+ fully_keyed: true,
+ }
+ }
+}
+
impl Deref for VList {
type Target = Vec;
@@ -23,41 +37,272 @@ impl Deref for VList {
impl DerefMut for VList {
fn deref_mut(&mut self) -> &mut Self::Target {
+ // Caller might change the keys of the VList or add unkeyed children.
+ // Defensively assume they will.
+ self.fully_keyed = false;
+
&mut self.children
}
}
+/// Log an operation during tests for debugging purposes
+/// Set RUSTFLAGS="--cfg verbose_tests" environment variable to activate.
+macro_rules! test_log {
+ ($fmt:literal, $($arg:expr),* $(,)?) => {
+ #[cfg(all(feature = "wasm_test", verbose_tests))]
+ ::wasm_bindgen_test::console_log!(concat!("\t ", $fmt), $($arg),*);
+ };
+}
+
+/// Advance the next sibling reference (from right to left) and log it for testing purposes
+/// Set RUSTFLAGS="--cfg verbose_tests" environment variable to activate.
+macro_rules! advance_next_sibling {
+ ($current:expr, $advance:expr) => {{
+ #[cfg(all(feature = "wasm_test", verbose_tests))]
+ let current = format!("{:?}", $current);
+ let next = $advance();
+ test_log!("advance next_sibling: {} -> {:?}", current, next);
+ next
+ }};
+}
+
impl VList {
- /// Creates a new empty `VList` instance.
+ /// Creates a new empty [VList] instance.
pub fn new() -> Self {
Self::default()
}
- /// Creates a new `VList` instance with children.
- pub fn new_with_children(children: Vec, key: Option) -> Self {
- VList { children, key }
+ /// Creates a new [VList] instance with children.
+ pub fn with_children(children: Vec, key: Option) -> Self {
+ VList {
+ fully_keyed: children.iter().all(|ch| ch.has_key()),
+ children,
+ key,
+ }
}
- /// Add `VNode` child.
+ /// Add [VNode] child.
pub fn add_child(&mut self, child: VNode) {
+ if self.fully_keyed && !child.has_key() {
+ self.fully_keyed = false;
+ }
self.children.push(child);
}
- /// Add multiple `VNode` children.
+ /// Add multiple [VNode] children.
pub fn add_children(&mut self, children: impl IntoIterator- ) {
- self.children.extend(children);
+ let it = children.into_iter();
+ let bound = it.size_hint();
+ self.children.reserve(bound.1.unwrap_or(bound.0));
+ for ch in it {
+ self.add_child(ch);
+ }
+ }
+
+ /// Recheck, if the all the children have keys.
+ ///
+ /// Run this, after modifying the child list that contained only keyed children prior to the
+ /// mutable dereference.
+ pub fn recheck_fully_keyed(&mut self) {
+ self.fully_keyed = self.children.iter().all(|ch| ch.has_key());
+ }
+
+ /// Diff and patch unkeyed child lists
+ fn apply_unkeyed(
+ parent_scope: &AnyScope,
+ parent: &Element,
+ mut next_sibling: NodeRef,
+ lefts: &mut [VNode],
+ rights: Vec,
+ ) -> NodeRef {
+ let mut diff = lefts.len() as isize - rights.len() as isize;
+ let mut lefts_it = lefts.iter_mut().rev();
+ let mut rights_it = rights.into_iter().rev();
+
+ macro_rules! apply {
+ ($l:expr, $r:expr) => {
+ test_log!("parent={:?}", parent.outer_html());
+ next_sibling = advance_next_sibling!(next_sibling, || $l.apply(
+ parent_scope,
+ parent,
+ next_sibling,
+ $r
+ ));
+ };
+ }
+
+ // Add missing nodes
+ while diff > 0 {
+ let l = lefts_it.next().unwrap();
+ test_log!("adding: {:?}", l);
+ apply!(l, None);
+ diff -= 1;
+ }
+ // Remove extra nodes
+ while diff < 0 {
+ let mut r = rights_it.next().unwrap();
+ test_log!("removing: {:?}", r);
+ r.detach(parent);
+ diff += 1;
+ }
+
+ for (l, r) in lefts_it.zip(rights_it) {
+ test_log!("patching: {:?} -> {:?}", r, l);
+ apply!(l, r.into());
+ }
+
+ next_sibling
}
- fn children_keys(&self, warn: bool) -> HashSet {
- let mut hash_set = HashSet::with_capacity(self.children.len());
- for l in self.children.iter() {
- if let Some(k) = l.key() {
- if !hash_set.insert(k.clone()) && warn {
- log::warn!("Key '{}' is not unique in list but must be.", k);
+ /// Diff and patch fully keyed child lists.
+ ///
+ /// Optimized for node addition or removal from either end of the list and small changes in the
+ /// middle.
+ fn apply_keyed(
+ parent_scope: &AnyScope,
+ parent: &Element,
+ mut next_sibling: NodeRef,
+ lefts: &mut [VNode],
+ rights: Vec,
+ ) -> NodeRef {
+ use std::mem::{transmute, MaybeUninit};
+
+ macro_rules! map_keys {
+ ($src:expr) => {
+ $src.iter()
+ .map(|v| v.key().expect("unkeyed child in fully keyed list"))
+ .collect::>()
+ };
+ }
+ let lefts_keys = map_keys!(lefts);
+ let rights_keys = map_keys!(rights);
+
+ /// Find the first differing key in 2 iterators
+ fn diff_i<'a, 'b>(
+ a: impl Iterator
- ,
+ b: impl Iterator
- ,
+ ) -> usize {
+ a.zip(b).take_while(|(a, b)| a == b).count()
+ }
+
+ // Find first key mismatch from the front
+ let from_start = diff_i(lefts_keys.iter(), rights_keys.iter());
+
+ if from_start == std::cmp::min(lefts.len(), rights.len()) {
+ // No key changes
+ return Self::apply_unkeyed(parent_scope, parent, next_sibling, lefts, rights);
+ }
+
+ // Find first key mismatch from the back
+ let from_end = diff_i(
+ lefts_keys[from_start..].iter().rev(),
+ rights_keys[from_start..].iter().rev(),
+ );
+
+ macro_rules! apply {
+ ($l:expr, $r:expr) => {
+ test_log!("patching: {:?} -> {:?}", $r, $l);
+ apply!(Some($r) => $l);
+ };
+ ($l:expr) => {
+ test_log!("adding: {:?}", $l);
+ apply!(None => $l);
+ };
+ ($ancestor:expr => $l:expr) => {
+ test_log!("parent={:?}", parent.outer_html());
+ next_sibling = advance_next_sibling!(
+ next_sibling,
+ || $l.apply(parent_scope, parent, next_sibling, $ancestor)
+ );
+ };
+ }
+
+ // We partially deconstruct the rights vector in several steps.
+ // This can not be done without default swapping or 2 big vector reallocation overhead in
+ // safe Rust.
+ let mut rights: Vec> = unsafe { transmute(rights) };
+
+ // Takes a value from the rights vector.
+ //
+ // We guarantee this is only done once because the consumer routine ranges don't overlap.
+ // We guarantee this is done for all nodes because all ranges are processed. There are no
+ // early returns (except panics) possible before full vector depletion.
+ macro_rules! take {
+ ($src:expr) => {
+ unsafe {
+ let mut dst = MaybeUninit::::uninit();
+ std::ptr::copy_nonoverlapping($src.as_mut_ptr(), dst.as_mut_ptr(), 1);
+ dst.assume_init()
+ }
+ };
+ }
+
+ // Diff matching children at the end
+ let lefts_to = lefts_keys.len() - from_end;
+ let rights_to = rights_keys.len() - from_end;
+ for (l, r) in lefts[lefts_to..]
+ .iter_mut()
+ .zip(rights[rights_to..].iter_mut())
+ .rev()
+ {
+ apply!(l, take!(r));
+ }
+
+ // Diff mismatched children in the middle
+ let mut next: Option<&Key> = None;
+ let mut rights_diff: HashMap<&Key, (VNode, Option<&Key>)> =
+ HashMap::with_capacity(rights_to - from_start);
+ for (k, v) in rights_keys[from_start..rights_to]
+ .iter()
+ .zip(rights[from_start..rights_to].iter_mut())
+ .rev()
+ {
+ rights_diff.insert(k, (take!(v), next.take()));
+ next = Some(k);
+ }
+ next = None;
+ for (l_k, l) in lefts_keys[from_start..lefts_to]
+ .iter()
+ .zip(lefts[from_start..lefts_to].iter_mut())
+ .rev()
+ {
+ match rights_diff.remove(l_k) {
+ // Reorder and diff any existing children
+ Some((r, r_next)) => {
+ match (r_next, next) {
+ // If the next sibling was already the same, we don't need to move the node
+ (Some(r_next), Some(l_next)) if r_next == l_next => (),
+ _ => {
+ test_log!("moving as next: {:?}", r);
+ r.move_before(parent, &next_sibling.get());
+ }
+ }
+ apply!(l, r);
+ }
+ // Add new children
+ None => {
+ apply!(l);
}
}
+ next = Some(l_k);
+ }
+
+ // Remove any extra rights
+ for (_, (mut r, _)) in rights_diff.drain() {
+ test_log!("removing: {:?}", r);
+ r.detach(parent);
}
- hash_set
+
+ // Diff matching children at the start
+ for (l, r) in lefts[..from_start]
+ .iter_mut()
+ .zip(rights[..from_start].iter_mut())
+ .rev()
+ {
+ apply!(l, take!(r));
+ }
+
+ next_sibling
}
}
@@ -88,143 +333,35 @@ impl VDiff for VList {
// Without a placeholder the next element becomes first
// and corrupts the order of rendering
// We use empty text element to stake out a place
- let placeholder = VText::new("");
- self.children.push(placeholder.into());
+ self.add_child(VText::new("").into());
}
- let left_keys = self.children_keys(true);
- let lefts_keyed = left_keys.len() == self.children.len();
+ let lefts = &mut self.children;
+ let (rights, rights_fully_keyed) = match ancestor {
+ // If the ancestor is also a VList, then the "right" list is the previously
+ // rendered items.
+ Some(VNode::VList(v)) => (v.children, v.fully_keyed),
- let right_keys = if let Some(VNode::VList(vlist)) = &ancestor {
- vlist.children_keys(false)
- } else {
- HashSet::new()
- };
-
- let mut right_children = match ancestor {
- // If the ancestor is also a VList, then the "right" list is the
- // previously rendered items.
- Some(VNode::VList(vlist)) => vlist.children,
// If the ancestor was not a VList, then the "right" list is a single node
- Some(vnode) => vec![vnode],
- None => vec![],
- };
- let rights_keyed = right_keys.len() == right_children.len();
-
- // If the existing list and the new list are both properly keyed,
- // then move existing list nodes into the new list's order before diffing
- if lefts_keyed && rights_keyed {
- // Find the intersection of keys to determine which right nodes can be reused
- let matched_keys: HashSet<_> = left_keys.intersection(&right_keys).collect();
-
- // Detach any right nodes that were not matched with a left node
- right_children = right_children
- .into_iter()
- .filter_map(|mut right| {
- if matched_keys.contains(right.key().as_ref().unwrap()) {
- Some(right)
- } else {
- right.detach(parent);
- None
- }
- })
- .collect();
-
- // Determine which rights are already in correct order and which
- // rights need to be moved in the DOM before being reused
- let mut rights_to_move = HashMap::with_capacity(right_children.len());
- let mut matched_lefts = self
- .children
- .iter()
- .filter(|left| matched_keys.contains(left.key().as_ref().unwrap()))
- .peekable();
- let mut left = matched_lefts.next();
-
- // Note: `filter_map` is used to move rights into `rights_to_move`
- #[allow(clippy::unnecessary_filter_map)]
- let rights_in_place: Vec<_> = right_children
- .into_iter()
- .filter_map(|right| {
- if right.key() == left.and_then(|l| l.key()) {
- left = matched_lefts.next();
- return Some(right);
- } else if right.key() == matched_lefts.peek().and_then(|l| l.key()) {
- matched_lefts.next();
- left = matched_lefts.next();
- return Some(right);
- }
-
- rights_to_move.insert(right.key().unwrap(), right);
- None
- })
- .collect();
-
- // Move rights into correct order and build `right_children`
- right_children = Vec::with_capacity(matched_keys.len());
- let mut matched_lefts = self
- .children
- .iter()
- .filter(|left| matched_keys.contains(left.key().as_ref().unwrap()));
-
- for right in rights_in_place.into_iter() {
- let mut left = matched_lefts.next().unwrap();
- while right.key() != left.key() {
- let right_to_move = rights_to_move.remove(&left.key().unwrap()).unwrap();
- right_to_move.move_before(parent, Some(right.first_node()));
- right_children.push(right_to_move);
- left = matched_lefts.next().unwrap();
- }
- right_children.push(right);
- }
-
- for left in matched_lefts {
- let right_to_move = rights_to_move.remove(&left.key().unwrap()).unwrap();
- right_to_move.move_before(parent, next_sibling.get());
- right_children.push(right_to_move);
+ Some(v) => {
+ let has_key = v.has_key();
+ (vec![v], has_key)
}
- assert!(rights_to_move.is_empty())
- }
-
- let mut rights = right_children.into_iter().peekable();
- let mut last_next_sibling = NodeRef::default();
- let mut nodes: Vec = self
- .children
- .iter_mut()
- .map(|left| {
- let ancestor = rights.next();
-
- // Create a new `next_sibling` reference which points to the next `right` or
- // the outer list's `next_sibling` if there are no more `rights`.
- let new_next_sibling = NodeRef::default();
- if let Some(next_right) = rights.peek() {
- new_next_sibling.set(Some(next_right.first_node()));
- } else {
- new_next_sibling.link(next_sibling.clone());
- }
-
- // Update the next list item and then link the previous left's `next_sibling` to the
- // returned `node` reference so that the previous left has an up-to-date `next_sibling`.
- // This is important for rendering a `VComp` because each `VComp` keeps track of its
- // `next_sibling` to properly render its children.
- let node = left.apply(parent_scope, parent, new_next_sibling.clone(), ancestor);
- last_next_sibling.link(node.clone());
- last_next_sibling = new_next_sibling;
- node
- })
- .collect();
-
- // If there are more `rights` than `lefts`, we need to make sure to link the last left's `next_sibling`
- // to the outer list's `next_sibling` so that it doesn't point at a `right` that is detached.
- last_next_sibling.link(next_sibling);
-
- // Detach all extra rights
- for mut right in rights {
- right.detach(parent);
- }
+ // No unkeyed nodes in an empty VList
+ _ => (vec![], true),
+ };
+ test_log!("lefts: {:?}", lefts);
+ test_log!("rights: {:?}", rights);
- assert!(!nodes.is_empty(), "VList should have at least one child");
- nodes.swap_remove(0)
+ #[allow(clippy::let_and_return)]
+ let first = if self.fully_keyed && rights_fully_keyed {
+ Self::apply_keyed(parent_scope, parent, next_sibling, lefts, rights)
+ } else {
+ Self::apply_unkeyed(parent_scope, parent, next_sibling, lefts, rights)
+ };
+ test_log!("result: {:?}", lefts);
+ first
}
}
diff --git a/packages/yew/src/virtual_dom/vnode.rs b/packages/yew/src/virtual_dom/vnode.rs
index 0012fee1a38..c259adfd99e 100644
--- a/packages/yew/src/virtual_dom/vnode.rs
+++ b/packages/yew/src/virtual_dom/vnode.rs
@@ -35,6 +35,16 @@ impl VNode {
}
}
+ /// Returns true if the [VNode] has a key without needlessly cloning the key.
+ pub fn has_key(&self) -> bool {
+ match self {
+ VNode::VComp(vcomp) => vcomp.key.is_some(),
+ VNode::VList(vlist) => vlist.key.is_some(),
+ VNode::VRef(_) | VNode::VText(_) => false,
+ VNode::VTag(vtag) => vtag.key.is_some(),
+ }
+ }
+
/// Returns the first DOM node that is used to designate the position of the virtual DOM node.
pub(crate) fn first_node(&self) -> Node {
match self {
@@ -48,20 +58,16 @@ impl VNode {
text_node.clone().into()
}
VNode::VComp(vcomp) => vcomp.node_ref.get().expect("VComp is not mounted"),
- VNode::VList(vlist) => vlist
- .children
- .get(0)
- .expect("VList is not mounted")
- .first_node(),
+ VNode::VList(vlist) => vlist.get(0).expect("VList is not mounted").first_node(),
VNode::VRef(node) => node.clone(),
}
}
- pub(crate) fn move_before(&self, parent: &Element, next_sibling: Option) {
+ pub(crate) fn move_before(&self, parent: &Element, next_sibling: &Option) {
match self {
VNode::VList(vlist) => {
- for node in vlist.children.iter() {
- node.move_before(parent, next_sibling.clone());
+ for node in vlist.iter() {
+ node.move_before(parent, next_sibling);
}
}
VNode::VComp(vcomp) => {
@@ -70,7 +76,7 @@ impl VNode {
.expect("VComp has no root vnode")
.move_before(parent, next_sibling);
}
- _ => super::insert_node(&self.first_node(), parent, next_sibling),
+ _ => super::insert_node(&self.first_node(), parent, next_sibling.as_ref()),
};
}
}
@@ -118,7 +124,7 @@ impl VDiff for VNode {
}
ancestor.detach(parent);
}
- super::insert_node(node, parent, next_sibling.get());
+ super::insert_node(node, parent, next_sibling.get().as_ref());
NodeRef::new(node.clone())
}
}
@@ -176,10 +182,10 @@ impl From for VNode {
impl> FromIterator for VNode {
fn from_iter>(iter: T) -> Self {
- VNode::VList(VList {
- key: None,
- children: iter.into_iter().map(|n| n.into()).collect(),
- })
+ VNode::VList(VList::with_children(
+ iter.into_iter().map(|n| n.into()).collect(),
+ None,
+ ))
}
}
@@ -190,7 +196,7 @@ impl fmt::Debug for VNode {
VNode::VText(ref vtext) => vtext.fmt(f),
VNode::VComp(ref vcomp) => vcomp.fmt(f),
VNode::VList(ref vlist) => vlist.fmt(f),
- VNode::VRef(ref vref) => vref.fmt(f),
+ VNode::VRef(ref vref) => write!(f, "VRef ( \"{}\" )", crate::utils::print_node(vref)),
}
}
}
diff --git a/packages/yew/src/virtual_dom/vtag.rs b/packages/yew/src/virtual_dom/vtag.rs
index 30a99a38857..32b75f97323 100644
--- a/packages/yew/src/virtual_dom/vtag.rs
+++ b/packages/yew/src/virtual_dom/vtag.rs
@@ -594,14 +594,14 @@ impl VDiff for VTag {
}
} else {
let el = self.create_element(parent);
- super::insert_node(&el, parent, Some(ancestor.first_node()));
+ super::insert_node(&el, parent, Some(&ancestor.first_node()));
ancestor.detach(parent);
(None, el)
}
}
None => (None, {
let el = self.create_element(parent);
- super::insert_node(&el, parent, next_sibling.get());
+ super::insert_node(&el, parent, next_sibling.get().as_ref());
el
}),
};
diff --git a/packages/yew/src/virtual_dom/vtext.rs b/packages/yew/src/virtual_dom/vtext.rs
index b33c2ef9b5f..a44ba7ac2a0 100644
--- a/packages/yew/src/virtual_dom/vtext.rs
+++ b/packages/yew/src/virtual_dom/vtext.rs
@@ -10,7 +10,7 @@ use web_sys::{Element, Text as TextNode};
/// A type for a virtual
/// [`TextNode`](https://developer.mozilla.org/en-US/docs/Web/API/Document/createTextNode)
/// representation.
-#[derive(Clone, Debug)]
+#[derive(Clone)]
pub struct VText {
/// Contains a text of the node.
pub text: AttrValue,
@@ -28,6 +28,20 @@ impl VText {
}
}
+impl std::fmt::Debug for VText {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(
+ f,
+ "VText {{ text: \"{}\", reference: {} }}",
+ self.text,
+ match &self.reference {
+ Some(_) => "Some(...)",
+ None => "None",
+ }
+ )
+ }
+}
+
impl VDiff for VText {
/// Remove VText from parent.
fn detach(&mut self, parent: &Element) {
@@ -66,7 +80,7 @@ impl VDiff for VText {
}
let text_node = document().create_text_node(&self.text);
- super::insert_node(&text_node, parent, next_sibling.get());
+ super::insert_node(&text_node, parent, next_sibling.get().as_ref());
self.reference = Some(text_node.clone());
NodeRef::new(text_node.into())
}