diff --git a/examples/futures/src/markdown.rs b/examples/futures/src/markdown.rs
index 5c85b7a55e4..afdad6bf98f 100644
--- a/examples/futures/src/markdown.rs
+++ b/examples/futures/src/markdown.rs
@@ -1,5 +1,7 @@
/// Original author of this code is [Nathan Ringo](https://github.com/remexre)
/// Source: https://github.com/acmumn/mentoring/blob/master/web-client/src/view/markdown.rs
+use std::rc::Rc;
+
use pulldown_cmark::{Alignment, CodeBlockKind, Event, Options, Parser, Tag};
use yew::virtual_dom::{VNode, VTag, VText};
use yew::{html, Classes, Html};
@@ -56,16 +58,22 @@ pub fn render_markdown(src: &str) -> Html {
if let Some(top_children) = top.children_mut() {
for r in top_children.to_vlist_mut().iter_mut() {
if let VNode::VTag(ref mut vtag) = r {
- if let Some(vtag_children) = vtag.children_mut() {
+ if let Some(vtag_children) = Rc::make_mut(vtag).children_mut() {
for (i, c) in
vtag_children.to_vlist_mut().iter_mut().enumerate()
{
if let VNode::VTag(ref mut vtag) = c {
match aligns[i] {
Alignment::None => {}
- Alignment::Left => add_class(vtag, "text-left"),
- Alignment::Center => add_class(vtag, "text-center"),
- Alignment::Right => add_class(vtag, "text-right"),
+ Alignment::Left => {
+ add_class(Rc::make_mut(vtag), "text-left")
+ }
+ Alignment::Center => {
+ add_class(Rc::make_mut(vtag), "text-center")
+ }
+ Alignment::Right => {
+ add_class(Rc::make_mut(vtag), "text-right")
+ }
}
}
}
@@ -79,7 +87,7 @@ pub fn render_markdown(src: &str) -> Html {
if let VNode::VTag(ref mut vtag) = c {
// TODO
// vtag.tag = "th".into();
- vtag.add_attribute("scope", "col");
+ Rc::make_mut(vtag).add_attribute("scope", "col");
}
}
}
@@ -99,7 +107,7 @@ pub fn render_markdown(src: &str) -> Html {
}
if elems.len() == 1 {
- VNode::VTag(Box::new(elems.pop().unwrap()))
+ VNode::VTag(Rc::new(elems.pop().unwrap()))
} else {
html! {
{ for elems.into_iter() }
diff --git a/packages/yew-macro/src/html_tree/html_element.rs b/packages/yew-macro/src/html_tree/html_element.rs
index 0124dd0c0d7..a501644941b 100644
--- a/packages/yew-macro/src/html_tree/html_element.rs
+++ b/packages/yew-macro/src/html_tree/html_element.rs
@@ -359,7 +359,7 @@ impl ToTokens for HtmlElement {
quote! {
::std::convert::Into::<::yew::virtual_dom::VNode>::into(
::yew::virtual_dom::VTag::__new_other(
- ::std::borrow::Cow::<'static, ::std::primitive::str>::Borrowed(#name),
+ ::yew::virtual_dom::AttrValue::Static(#name),
#node_ref,
#key,
#attributes,
@@ -416,7 +416,7 @@ impl ToTokens for HtmlElement {
// (note the extra braces). Hence the need for the `allow`.
// Anyways to remove the braces?
let mut #vtag_name = ::std::convert::Into::<
- ::std::borrow::Cow::<'static, ::std::primitive::str>
+ ::yew::virtual_dom::AttrValue
>::into(#expr);
::std::debug_assert!(
#vtag_name.is_ascii(),
diff --git a/packages/yew-macro/src/html_tree/html_list.rs b/packages/yew-macro/src/html_tree/html_list.rs
index e48f250bfde..301b533a92c 100644
--- a/packages/yew-macro/src/html_tree/html_list.rs
+++ b/packages/yew-macro/src/html_tree/html_list.rs
@@ -78,9 +78,9 @@ impl ToTokens for HtmlList {
};
tokens.extend(quote_spanned! {spanned.span()=>
- ::yew::virtual_dom::VNode::VList(
+ ::yew::virtual_dom::VNode::VList(::std::rc::Rc::new(
::yew::virtual_dom::VList::with_children(#children, #key)
- )
+ ))
});
}
}
diff --git a/packages/yew-macro/src/html_tree/mod.rs b/packages/yew-macro/src/html_tree/mod.rs
index 0361231b6d4..266ea31bfd8 100644
--- a/packages/yew-macro/src/html_tree/mod.rs
+++ b/packages/yew-macro/src/html_tree/mod.rs
@@ -123,7 +123,7 @@ impl ToTokens for HtmlTree {
lint::lint_all(self);
match self {
HtmlTree::Empty => tokens.extend(quote! {
- ::yew::virtual_dom::VNode::VList(::yew::virtual_dom::VList::new())
+ <::yew::virtual_dom::VNode as ::std::default::Default>::default()
}),
HtmlTree::Component(comp) => comp.to_tokens(tokens),
HtmlTree::Element(tag) => tag.to_tokens(tokens),
@@ -375,9 +375,9 @@ impl ToTokens for HtmlRootBraced {
tokens.extend(quote_spanned! {brace.span.span()=>
{
- ::yew::virtual_dom::VNode::VList(
+ ::yew::virtual_dom::VNode::VList(::std::rc::Rc::new(
::yew::virtual_dom::VList::with_children(#children, ::std::option::Option::None)
- )
+ ))
}
});
}
diff --git a/packages/yew-macro/tests/html_macro/component-any-children-pass.rs b/packages/yew-macro/tests/html_macro/component-any-children-pass.rs
index cf5a924e506..c9b9e0f1777 100644
--- a/packages/yew-macro/tests/html_macro/component-any-children-pass.rs
+++ b/packages/yew-macro/tests/html_macro/component-any-children-pass.rs
@@ -83,12 +83,12 @@ impl ::std::convert::From<::yew::virtual_dom::VChild> for ChildrenVari
impl ::std::convert::Into<::yew::virtual_dom::VNode> for ChildrenVariants {
fn into(self) -> ::yew::virtual_dom::VNode {
match self {
- Self::Child(comp) => ::yew::virtual_dom::VNode::VComp(::std::convert::Into::<
+ Self::Child(comp) => ::yew::virtual_dom::VNode::VComp(::std::rc::Rc::new(::std::convert::Into::<
::yew::virtual_dom::VComp,
- >::into(comp)),
- Self::AltChild(comp) => ::yew::virtual_dom::VNode::VComp(::std::convert::Into::<
+ >::into(comp))),
+ Self::AltChild(comp) => ::yew::virtual_dom::VNode::VComp(::std::rc::Rc::new(::std::convert::Into::<
::yew::virtual_dom::VComp,
- >::into(comp)),
+ >::into(comp))),
}
}
}
diff --git a/packages/yew-macro/tests/html_macro/component-pass.rs b/packages/yew-macro/tests/html_macro/component-pass.rs
index 61f0de060f8..f971f66c4cb 100644
--- a/packages/yew-macro/tests/html_macro/component-pass.rs
+++ b/packages/yew-macro/tests/html_macro/component-pass.rs
@@ -82,12 +82,12 @@ impl ::std::convert::From<::yew::virtual_dom::VChild> for ChildrenVari
impl ::std::convert::Into<::yew::virtual_dom::VNode> for ChildrenVariants {
fn into(self) -> ::yew::virtual_dom::VNode {
match self {
- Self::Child(comp) => ::yew::virtual_dom::VNode::VComp(::std::convert::Into::<
+ Self::Child(comp) => ::yew::virtual_dom::VNode::VComp(::std::rc::Rc::new(::std::convert::Into::<
::yew::virtual_dom::VComp,
- >::into(comp)),
- Self::AltChild(comp) => ::yew::virtual_dom::VNode::VComp(::std::convert::Into::<
+ >::into(comp))),
+ Self::AltChild(comp) => ::yew::virtual_dom::VNode::VComp(::std::rc::Rc::new(::std::convert::Into::<
::yew::virtual_dom::VComp,
- >::into(comp)),
+ >::into(comp))),
}
}
}
diff --git a/packages/yew-macro/tests/html_macro/element-fail.stderr b/packages/yew-macro/tests/html_macro/element-fail.stderr
index d05a7cfad41..842553fab43 100644
--- a/packages/yew-macro/tests/html_macro/element-fail.stderr
+++ b/packages/yew-macro/tests/html_macro/element-fail.stderr
@@ -703,20 +703,15 @@ error[E0277]: the trait bound `(): IntoPropValue` is not satisfied
and $N others
= note: this error originates in the macro `html` (in Nightly builds, run with -Z macro-backtrace for more info)
-error[E0277]: the trait bound `Cow<'static, str>: From<{integer}>` is not satisfied
+error[E0277]: the trait bound `implicit_clone::unsync::IString: From<{integer}>` is not satisfied
--> tests/html_macro/element-fail.rs:77:15
|
77 | html! { <@{55}>@> };
- | ^^^^ the trait `From<{integer}>` is not implemented for `Cow<'static, str>`
+ | ^^^^ the trait `From<{integer}>` is not implemented for `implicit_clone::unsync::IString`
|
= help: the following other types implement trait `From`:
- as From<&'a CStr>>
- as From<&'a CString>>
- as From>
- as From<&'a OsStr>>
- as From<&'a OsString>>
- as From>
- as From<&'a Path>>
- as From<&'a PathBuf>>
- and $N others
- = note: required because of the requirements on the impl of `Into>` for `{integer}`
+ >
+ >>
+ >>
+ >
+ = note: required because of the requirements on the impl of `Into` for `{integer}`
diff --git a/packages/yew/src/dom_bundle/blist.rs b/packages/yew/src/dom_bundle/blist.rs
index 93a9af585a8..ddd909027ea 100644
--- a/packages/yew/src/dom_bundle/blist.rs
+++ b/packages/yew/src/dom_bundle/blist.rs
@@ -4,13 +4,13 @@ use std::cmp::Ordering;
use std::collections::HashSet;
use std::hash::Hash;
use std::ops::Deref;
-use std::rc::Rc;
use web_sys::Element;
use super::{test_log, BNode, BSubtree, DomSlot};
use crate::dom_bundle::{Reconcilable, ReconcileTarget};
use crate::html::AnyScope;
+use crate::utils::RcExt;
use crate::virtual_dom::{Key, VList, VNode, VText};
/// This struct represents a mounted [VList]
@@ -30,10 +30,8 @@ impl VList {
let children = self
.children
- .map(Rc::try_unwrap)
- .unwrap_or_else(|| Ok(Vec::new()))
- // Rc::unwrap_or_clone is not stable yet.
- .unwrap_or_else(|m| m.to_vec());
+ .map(RcExt::unwrap_or_clone)
+ .unwrap_or_default();
(self.key, fully_keyed, children)
}
diff --git a/packages/yew/src/dom_bundle/bnode.rs b/packages/yew/src/dom_bundle/bnode.rs
index d6fdbe5aa47..2f59acfe47f 100644
--- a/packages/yew/src/dom_bundle/bnode.rs
+++ b/packages/yew/src/dom_bundle/bnode.rs
@@ -7,6 +7,7 @@ use web_sys::{Element, Node};
use super::{BComp, BList, BPortal, BRaw, BSubtree, BSuspense, BTag, BText, DomSlot};
use crate::dom_bundle::{Reconcilable, ReconcileTarget};
use crate::html::AnyScope;
+use crate::utils::RcExt;
use crate::virtual_dom::{Key, VNode};
/// The bundle implementation to [VNode].
@@ -95,7 +96,8 @@ impl Reconcilable for VNode {
) -> (DomSlot, Self::Bundle) {
match self {
VNode::VTag(vtag) => {
- let (node_ref, tag) = vtag.attach(root, parent_scope, parent, slot);
+ let (node_ref, tag) =
+ RcExt::unwrap_or_clone(vtag).attach(root, parent_scope, parent, slot);
(node_ref, tag.into())
}
VNode::VText(vtext) => {
@@ -103,11 +105,13 @@ impl Reconcilable for VNode {
(node_ref, text.into())
}
VNode::VComp(vcomp) => {
- let (node_ref, comp) = vcomp.attach(root, parent_scope, parent, slot);
+ let (node_ref, comp) =
+ RcExt::unwrap_or_clone(vcomp).attach(root, parent_scope, parent, slot);
(node_ref, comp.into())
}
VNode::VList(vlist) => {
- let (node_ref, list) = vlist.attach(root, parent_scope, parent, slot);
+ let (node_ref, list) =
+ RcExt::unwrap_or_clone(vlist).attach(root, parent_scope, parent, slot);
(node_ref, list.into())
}
VNode::VRef(node) => {
@@ -115,11 +119,13 @@ impl Reconcilable for VNode {
(DomSlot::at(node.clone()), BNode::Ref(node))
}
VNode::VPortal(vportal) => {
- let (node_ref, portal) = vportal.attach(root, parent_scope, parent, slot);
+ let (node_ref, portal) =
+ RcExt::unwrap_or_clone(vportal).attach(root, parent_scope, parent, slot);
(node_ref, portal.into())
}
VNode::VSuspense(vsuspsense) => {
- let (node_ref, suspsense) = vsuspsense.attach(root, parent_scope, parent, slot);
+ let (node_ref, suspsense) =
+ RcExt::unwrap_or_clone(vsuspsense).attach(root, parent_scope, parent, slot);
(node_ref, suspsense.into())
}
VNode::VRaw(vraw) => {
@@ -149,20 +155,46 @@ impl Reconcilable for VNode {
bundle: &mut BNode,
) -> DomSlot {
match self {
- VNode::VTag(vtag) => vtag.reconcile_node(root, parent_scope, parent, slot, bundle),
+ VNode::VTag(vtag) => RcExt::unwrap_or_clone(vtag).reconcile_node(
+ root,
+ parent_scope,
+ parent,
+ slot,
+ bundle,
+ ),
VNode::VText(vtext) => vtext.reconcile_node(root, parent_scope, parent, slot, bundle),
- VNode::VComp(vcomp) => vcomp.reconcile_node(root, parent_scope, parent, slot, bundle),
- VNode::VList(vlist) => vlist.reconcile_node(root, parent_scope, parent, slot, bundle),
+ VNode::VComp(vcomp) => RcExt::unwrap_or_clone(vcomp).reconcile_node(
+ root,
+ parent_scope,
+ parent,
+ slot,
+ bundle,
+ ),
+ VNode::VList(vlist) => RcExt::unwrap_or_clone(vlist).reconcile_node(
+ root,
+ parent_scope,
+ parent,
+ slot,
+ bundle,
+ ),
VNode::VRef(node) => match bundle {
BNode::Ref(ref n) if &node == n => DomSlot::at(node),
_ => VNode::VRef(node).replace(root, parent_scope, parent, slot, bundle),
},
- VNode::VPortal(vportal) => {
- vportal.reconcile_node(root, parent_scope, parent, slot, bundle)
- }
- VNode::VSuspense(vsuspsense) => {
- vsuspsense.reconcile_node(root, parent_scope, parent, slot, bundle)
- }
+ VNode::VPortal(vportal) => RcExt::unwrap_or_clone(vportal).reconcile_node(
+ root,
+ parent_scope,
+ parent,
+ slot,
+ bundle,
+ ),
+ VNode::VSuspense(vsuspsense) => RcExt::unwrap_or_clone(vsuspsense).reconcile_node(
+ root,
+ parent_scope,
+ parent,
+ slot,
+ bundle,
+ ),
VNode::VRaw(vraw) => vraw.reconcile_node(root, parent_scope, parent, slot, bundle),
}
}
@@ -246,10 +278,16 @@ mod feat_hydration {
fragment: &mut Fragment,
) -> Self::Bundle {
match self {
- VNode::VTag(vtag) => vtag.hydrate(root, parent_scope, parent, fragment).into(),
+ VNode::VTag(vtag) => RcExt::unwrap_or_clone(vtag)
+ .hydrate(root, parent_scope, parent, fragment)
+ .into(),
VNode::VText(vtext) => vtext.hydrate(root, parent_scope, parent, fragment).into(),
- VNode::VComp(vcomp) => vcomp.hydrate(root, parent_scope, parent, fragment).into(),
- VNode::VList(vlist) => vlist.hydrate(root, parent_scope, parent, fragment).into(),
+ VNode::VComp(vcomp) => RcExt::unwrap_or_clone(vcomp)
+ .hydrate(root, parent_scope, parent, fragment)
+ .into(),
+ VNode::VList(vlist) => RcExt::unwrap_or_clone(vlist)
+ .hydrate(root, parent_scope, parent, fragment)
+ .into(),
// You cannot hydrate a VRef.
VNode::VRef(_) => {
panic!(
@@ -264,7 +302,7 @@ mod feat_hydration {
use_effect."
)
}
- VNode::VSuspense(vsuspense) => vsuspense
+ VNode::VSuspense(vsuspense) => RcExt::unwrap_or_clone(vsuspense)
.hydrate(root, parent_scope, parent, fragment)
.into(),
VNode::VRaw(vraw) => vraw.hydrate(root, parent_scope, parent, fragment).into(),
diff --git a/packages/yew/src/dom_bundle/bportal.rs b/packages/yew/src/dom_bundle/bportal.rs
index a7e5d76c9a0..9792383101b 100644
--- a/packages/yew/src/dom_bundle/bportal.rs
+++ b/packages/yew/src/dom_bundle/bportal.rs
@@ -123,6 +123,8 @@ impl BPortal {
mod layout_tests {
extern crate self as yew;
+ use std::rc::Rc;
+
use gloo::utils::document;
use wasm_bindgen_test::{wasm_bindgen_test as test, wasm_bindgen_test_configure};
use web_sys::HtmlInputElement;
@@ -151,10 +153,10 @@ mod layout_tests {
{VNode::VRef(first_target.clone().into())}
{VNode::VRef(second_target.clone().into())}
- {VNode::VPortal(VPortal::new(
+ {VNode::VPortal(Rc::new(VPortal::new(
html! { {"PORTAL"} },
first_target.clone(),
- ))}
+ )))}
{"AFTER"}
},
@@ -166,10 +168,10 @@ mod layout_tests {
{VNode::VRef(first_target.clone().into())}
{VNode::VRef(second_target.clone().into())}
- {VNode::VPortal(VPortal::new(
+ {VNode::VPortal(Rc::new(VPortal::new(
html! { {"PORTAL"} },
second_target.clone(),
- ))}
+ )))}
{"AFTER"}
},
@@ -181,10 +183,10 @@ mod layout_tests {
{VNode::VRef(first_target.clone().into())}
{VNode::VRef(second_target.clone().into())}
- {VNode::VPortal(VPortal::new(
+ {VNode::VPortal(Rc::new(VPortal::new(
html! { <> {"PORTAL"} > },
second_target.clone(),
- ))}
+ )))}
{"AFTER"}
},
@@ -207,11 +209,11 @@ mod layout_tests {
node: html! {
{VNode::VRef(target_with_child.clone().into())}
- {VNode::VPortal(VPortal::new_before(
+ {VNode::VPortal(Rc::new(VPortal::new_before(
html! { {"PORTAL"} },
target_with_child.clone(),
Some(target_child.clone().into()),
- ))}
+ )))}
},
expected: "PORTAL
",
diff --git a/packages/yew/src/dom_bundle/btag/mod.rs b/packages/yew/src/dom_bundle/btag/mod.rs
index c590d6be6c5..8a97c1ef33d 100644
--- a/packages/yew/src/dom_bundle/btag/mod.rs
+++ b/packages/yew/src/dom_bundle/btag/mod.rs
@@ -3,7 +3,6 @@
mod attributes;
mod listeners;
-use std::borrow::Cow;
use std::cell::RefCell;
use std::collections::HashMap;
use std::hint::unreachable_unchecked;
@@ -18,7 +17,7 @@ use web_sys::{Element, HtmlTextAreaElement as TextAreaElement};
use super::{BNode, BSubtree, DomSlot, Reconcilable, ReconcileTarget};
use crate::html::AnyScope;
use crate::virtual_dom::vtag::{InputFields, VTagInner, Value, MATHML_NAMESPACE, SVG_NAMESPACE};
-use crate::virtual_dom::{Attributes, Key, VTag};
+use crate::virtual_dom::{AttrValue, Attributes, Key, VTag};
use crate::NodeRef;
/// Applies contained changes to DOM [web_sys::Element]
@@ -51,7 +50,7 @@ enum BTagInner {
/// Fields for all other kinds of [VTag]s
Other {
/// A tag of the element.
- tag: Cow<'static, str>,
+ tag: AttrValue,
/// Child node.
child_bundle: BNode,
},
@@ -407,6 +406,8 @@ mod feat_hydration {
#[cfg(target_arch = "wasm32")]
#[cfg(test)]
mod tests {
+ use std::rc::Rc;
+
use wasm_bindgen::JsCast;
use wasm_bindgen_test::{wasm_bindgen_test as test, wasm_bindgen_test_configure};
use web_sys::HtmlInputElement as InputElement;
@@ -414,6 +415,7 @@ mod tests {
use super::*;
use crate::dom_bundle::utils::setup_parent;
use crate::dom_bundle::{BNode, Reconcilable, ReconcileTarget};
+ use crate::utils::RcExt;
use crate::virtual_dom::vtag::{HTML_NAMESPACE, SVG_NAMESPACE};
use crate::virtual_dom::{AttrValue, VNode, VTag};
use crate::{html, Html, NodeRef};
@@ -564,7 +566,7 @@ mod tests {
fn assert_vtag(node: VNode) -> VTag {
if let VNode::VTag(vtag) = node {
- return *vtag;
+ return RcExt::unwrap_or_clone(vtag);
}
panic!("should be vtag");
}
@@ -971,7 +973,7 @@ mod tests {
vtag.add_attribute("disabled", "disabled");
vtag.add_attribute("tabindex", "0");
- let elem = VNode::VTag(Box::new(vtag));
+ let elem = VNode::VTag(Rc::new(vtag));
let (_, mut elem) = elem.attach(&root, &scope, &parent, DomSlot::at_end());
@@ -979,7 +981,7 @@ mod tests {
let mut vtag = VTag::new("div");
vtag.node_ref = test_ref.clone();
vtag.add_attribute("tabindex", "0");
- let next_elem = VNode::VTag(Box::new(vtag));
+ let next_elem = VNode::VTag(Rc::new(vtag));
let elem_vtag = assert_vtag(next_elem);
// Sync happens here
diff --git a/packages/yew/src/html/classes.rs b/packages/yew/src/html/classes.rs
index 0a5bb9cc9b0..941ec617bf2 100644
--- a/packages/yew/src/html/classes.rs
+++ b/packages/yew/src/html/classes.rs
@@ -6,6 +6,7 @@ use implicit_clone::ImplicitClone;
use indexmap::IndexSet;
use super::IntoPropValue;
+use crate::utils::RcExt;
use crate::virtual_dom::AttrValue;
/// A set of classes, cheap to clone.
@@ -150,10 +151,7 @@ impl IntoIterator for Classes {
#[inline]
fn into_iter(self) -> Self::IntoIter {
- // NOTE: replace this by Rc::unwrap_or_clone() when it becomes stable
- Rc::try_unwrap(self.set)
- .unwrap_or_else(|rc| (*rc).clone())
- .into_iter()
+ RcExt::unwrap_or_clone(self.set).into_iter()
}
}
diff --git a/packages/yew/src/html/component/children.rs b/packages/yew/src/html/component/children.rs
index 5118c99f84b..af004d96ff9 100644
--- a/packages/yew/src/html/component/children.rs
+++ b/packages/yew/src/html/component/children.rs
@@ -1,6 +1,7 @@
//! Component children module
use std::fmt;
+use std::rc::Rc;
use crate::html::Html;
use crate::virtual_dom::{VChild, VComp, VList, VNode};
@@ -241,7 +242,7 @@ impl From> for Html {
}
}
- Html::VList(val.into())
+ Html::VList(Rc::new(val.into()))
}
}
diff --git a/packages/yew/src/html/conversion/to_html.rs b/packages/yew/src/html/conversion/to_html.rs
index c7d3eeaa57b..af15d5a179e 100644
--- a/packages/yew/src/html/conversion/to_html.rs
+++ b/packages/yew/src/html/conversion/to_html.rs
@@ -46,18 +46,18 @@ where
{
#[inline(always)]
fn to_html(&self) -> Html {
- Html::VList(VList::with_children(
+ Html::VList(Rc::new(VList::with_children(
self.iter().map(ToHtml::to_html).collect(),
None,
- ))
+ )))
}
#[inline(always)]
fn into_html(self) -> Html {
- Html::VList(VList::with_children(
+ Html::VList(Rc::new(VList::with_children(
self.into_iter().map(ToHtml::into_html).collect(),
None,
- ))
+ )))
}
}
@@ -81,7 +81,7 @@ impl ToHtml for Vec {
#[inline(always)]
fn into_html(self) -> Html {
- Html::VList(VList::with_children(self, None))
+ Html::VList(Rc::new(VList::with_children(self, None)))
}
}
@@ -105,7 +105,7 @@ impl ToHtml for VList {
#[inline(always)]
fn into_html(self) -> Html {
- Html::VList(self)
+ Html::VList(Rc::new(self))
}
}
@@ -132,7 +132,7 @@ where
#[inline(always)]
fn into_html(self) -> Html {
- VNode::VComp(self.into())
+ VNode::VComp(Rc::new(self.into()))
}
}
diff --git a/packages/yew/src/html/mod.rs b/packages/yew/src/html/mod.rs
index 87e3cc03372..cbb0b1c5488 100644
--- a/packages/yew/src/html/mod.rs
+++ b/packages/yew/src/html/mod.rs
@@ -142,5 +142,5 @@ mod feat_csr {
/// ## Relevant examples
/// - [Portals](https://github.com/yewstack/yew/tree/master/examples/portals)
pub fn create_portal(child: Html, host: Element) -> Html {
- VNode::VPortal(VPortal::new(child, host))
+ VNode::VPortal(Rc::new(VPortal::new(child, host)))
}
diff --git a/packages/yew/src/utils/mod.rs b/packages/yew/src/utils/mod.rs
index 98934ed054b..4f33ea672b2 100644
--- a/packages/yew/src/utils/mod.rs
+++ b/packages/yew/src/utils/mod.rs
@@ -64,3 +64,14 @@ pub fn print_node(n: &web_sys::Node) -> String {
None => n.text_content().unwrap_or_default(),
}
}
+
+// NOTE: replace this by Rc::unwrap_or_clone() when it becomes stable
+pub(crate) trait RcExt {
+ fn unwrap_or_clone(this: Self) -> T;
+}
+
+impl RcExt for std::rc::Rc {
+ fn unwrap_or_clone(this: Self) -> T {
+ std::rc::Rc::try_unwrap(this).unwrap_or_else(|rc| (*rc).clone())
+ }
+}
diff --git a/packages/yew/src/virtual_dom/listeners.rs b/packages/yew/src/virtual_dom/listeners.rs
index caa0943d599..f96dead2f39 100644
--- a/packages/yew/src/virtual_dom/listeners.rs
+++ b/packages/yew/src/virtual_dom/listeners.rs
@@ -1,5 +1,7 @@
use std::rc::Rc;
+use crate::html::ImplicitClone;
+
/// The [Listener] trait is an universal implementation of an event listener
/// which is used to bind Rust-listener to JS-listener (DOM).
pub trait Listener {
@@ -168,6 +170,8 @@ pub enum Listeners {
Pending(Box<[Option>]>),
}
+impl ImplicitClone for Listeners {}
+
impl PartialEq for Listeners {
fn eq(&self, rhs: &Self) -> bool {
use Listeners::*;
diff --git a/packages/yew/src/virtual_dom/mod.rs b/packages/yew/src/virtual_dom/mod.rs
index 940c46021f9..6ede1e6e074 100644
--- a/packages/yew/src/virtual_dom/mod.rs
+++ b/packages/yew/src/virtual_dom/mod.rs
@@ -22,6 +22,7 @@ pub mod vtag;
pub mod vtext;
use std::hint::unreachable_unchecked;
+use std::rc::Rc;
use indexmap::IndexMap;
@@ -204,7 +205,7 @@ pub enum Attributes {
/// IndexMap is used to provide runtime attribute deduplication in cases where the html! macro
/// was not used to guarantee it.
- IndexMap(IndexMap),
+ IndexMap(Rc>),
}
impl Attributes {
@@ -233,7 +234,7 @@ impl Attributes {
macro_rules! unpack {
() => {
match self {
- Self::IndexMap(m) => m,
+ Self::IndexMap(m) => Rc::make_mut(m),
// SAFETY: unreachable because we set self to the `IndexMap` variant above.
_ => unsafe { unreachable_unchecked() },
}
@@ -241,23 +242,23 @@ impl Attributes {
}
match self {
- Self::IndexMap(m) => m,
+ Self::IndexMap(m) => Rc::make_mut(m),
Self::Static(arr) => {
- *self = Self::IndexMap(
+ *self = Self::IndexMap(Rc::new(
arr.iter()
.map(|(k, v, ty)| ((*k).into(), ((*v).into(), *ty)))
.collect(),
- );
+ ));
unpack!()
}
Self::Dynamic { keys, values } => {
- *self = Self::IndexMap(
+ *self = Self::IndexMap(Rc::new(
std::mem::take(values)
.iter_mut()
.zip(keys.iter())
.filter_map(|(v, k)| v.take().map(|v| (AttrValue::from(*k), v)))
.collect(),
- );
+ ));
unpack!()
}
}
@@ -270,7 +271,7 @@ impl From> for Attributes {
.into_iter()
.map(|(k, v)| (k, (v, ApplyAttributeAs::Attribute)))
.collect();
- Self::IndexMap(v)
+ Self::IndexMap(Rc::new(v))
}
}
@@ -280,7 +281,7 @@ impl From> for Attributes {
.into_iter()
.map(|(k, v)| (AttrValue::Static(k), (v, ApplyAttributeAs::Attribute)))
.collect();
- Self::IndexMap(v)
+ Self::IndexMap(Rc::new(v))
}
}
diff --git a/packages/yew/src/virtual_dom/vlist.rs b/packages/yew/src/virtual_dom/vlist.rs
index cd9459497f1..62dc5b14e0a 100644
--- a/packages/yew/src/virtual_dom/vlist.rs
+++ b/packages/yew/src/virtual_dom/vlist.rs
@@ -3,6 +3,7 @@ use std::ops::{Deref, DerefMut};
use std::rc::Rc;
use super::{Key, VNode};
+use crate::html::ImplicitClone;
#[derive(Clone, Copy, Debug, PartialEq)]
enum FullyKeyedState {
@@ -23,6 +24,8 @@ pub struct VList {
pub key: Option,
}
+impl ImplicitClone for VList {}
+
impl PartialEq for VList {
fn eq(&self, other: &Self) -> bool {
self.key == other.key && self.children == other.children
diff --git a/packages/yew/src/virtual_dom/vnode.rs b/packages/yew/src/virtual_dom/vnode.rs
index 943081c2f3e..8ed18f29c9f 100644
--- a/packages/yew/src/virtual_dom/vnode.rs
+++ b/packages/yew/src/virtual_dom/vnode.rs
@@ -2,12 +2,13 @@
use std::cmp::PartialEq;
use std::iter::FromIterator;
+use std::rc::Rc;
use std::{fmt, mem};
use web_sys::Node;
use super::{Key, VChild, VComp, VList, VPortal, VSuspense, VTag, VText};
-use crate::html::BaseComponent;
+use crate::html::{BaseComponent, ImplicitClone};
use crate::virtual_dom::VRaw;
use crate::AttrValue;
@@ -16,25 +17,27 @@ use crate::AttrValue;
#[must_use = "html does not do anything unless returned to Yew for rendering."]
pub enum VNode {
/// A bind between `VTag` and `Element`.
- VTag(Box),
+ VTag(Rc),
/// A bind between `VText` and `TextNode`.
VText(VText),
/// A bind between `VComp` and `Element`.
- VComp(VComp),
+ VComp(Rc),
/// A holder for a list of other nodes.
- VList(VList),
+ VList(Rc),
/// A portal to another part of the document
- VPortal(VPortal),
+ VPortal(Rc),
/// A holder for any `Node` (necessary for replacing node).
VRef(Node),
/// A suspendible document fragment.
- VSuspense(VSuspense),
+ VSuspense(Rc),
/// A raw HTML string, represented by [`AttrValue`](crate::AttrValue).
///
/// Also see: [`VNode::from_html_unchecked`]
VRaw(VRaw),
}
+impl ImplicitClone for VNode {}
+
impl VNode {
pub fn key(&self) -> Option<&Key> {
match self {
@@ -60,9 +63,10 @@ impl VNode {
pub fn to_vlist_mut(&mut self) -> &mut VList {
loop {
match *self {
- Self::VList(ref mut m) => return m,
+ Self::VList(ref mut m) => return Rc::make_mut(m),
_ => {
- *self = VNode::VList(VList::with_children(vec![mem::take(self)], None));
+ *self =
+ VNode::VList(Rc::new(VList::with_children(vec![mem::take(self)], None)));
}
}
}
@@ -105,7 +109,7 @@ impl VNode {
impl Default for VNode {
fn default() -> Self {
- VNode::VList(VList::default())
+ VNode::VList(Rc::new(VList::default()))
}
}
@@ -119,35 +123,35 @@ impl From for VNode {
impl From for VNode {
#[inline]
fn from(vlist: VList) -> Self {
- VNode::VList(vlist)
+ VNode::VList(Rc::new(vlist))
}
}
impl From for VNode {
#[inline]
fn from(vtag: VTag) -> Self {
- VNode::VTag(Box::new(vtag))
+ VNode::VTag(Rc::new(vtag))
}
}
impl From for VNode {
#[inline]
fn from(vcomp: VComp) -> Self {
- VNode::VComp(vcomp)
+ VNode::VComp(Rc::new(vcomp))
}
}
impl From for VNode {
#[inline]
fn from(vsuspense: VSuspense) -> Self {
- VNode::VSuspense(vsuspense)
+ VNode::VSuspense(Rc::new(vsuspense))
}
}
impl From for VNode {
#[inline]
fn from(vportal: VPortal) -> Self {
- VNode::VPortal(vportal)
+ VNode::VPortal(Rc::new(vportal))
}
}
@@ -156,7 +160,7 @@ where
COMP: BaseComponent,
{
fn from(vchild: VChild) -> Self {
- VNode::VComp(VComp::from(vchild))
+ VNode::VComp(Rc::new(VComp::from(vchild)))
}
}
@@ -168,10 +172,10 @@ impl From for VNode {
impl> FromIterator for VNode {
fn from_iter>(iter: T) -> Self {
- VNode::VList(VList::with_children(
+ VNode::VList(Rc::new(VList::with_children(
iter.into_iter().map(|n| n.into()).collect(),
None,
- ))
+ )))
}
}
diff --git a/packages/yew/src/virtual_dom/vportal.rs b/packages/yew/src/virtual_dom/vportal.rs
index b0b7b301f5a..ce39e82b9dd 100644
--- a/packages/yew/src/virtual_dom/vportal.rs
+++ b/packages/yew/src/virtual_dom/vportal.rs
@@ -8,10 +8,10 @@ use super::VNode;
pub struct VPortal {
/// The element under which the content is inserted.
pub host: Element,
- /// The next sibling after the inserted content. Most be a child of `host`.
+ /// The next sibling after the inserted content. Must be a child of `host`.
pub inner_sibling: Option,
/// The inserted node
- pub node: Box,
+ pub node: VNode,
}
impl VPortal {
@@ -20,7 +20,7 @@ impl VPortal {
Self {
host,
inner_sibling: None,
- node: Box::new(content),
+ node: content,
}
}
@@ -31,7 +31,7 @@ impl VPortal {
Self {
host,
inner_sibling,
- node: Box::new(content),
+ node: content,
}
}
}
diff --git a/packages/yew/src/virtual_dom/vraw.rs b/packages/yew/src/virtual_dom/vraw.rs
index 366e5248f00..9c8e7a3c46a 100644
--- a/packages/yew/src/virtual_dom/vraw.rs
+++ b/packages/yew/src/virtual_dom/vraw.rs
@@ -1,3 +1,4 @@
+use crate::html::ImplicitClone;
use crate::AttrValue;
/// A raw HTML string to be used in VDOM.
@@ -6,6 +7,8 @@ pub struct VRaw {
pub html: AttrValue,
}
+impl ImplicitClone for VRaw {}
+
impl From for VRaw {
fn from(html: AttrValue) -> Self {
Self { html }
diff --git a/packages/yew/src/virtual_dom/vsuspense.rs b/packages/yew/src/virtual_dom/vsuspense.rs
index 1ae97b8603a..8167f4fb2e1 100644
--- a/packages/yew/src/virtual_dom/vsuspense.rs
+++ b/packages/yew/src/virtual_dom/vsuspense.rs
@@ -1,23 +1,26 @@
use super::{Key, VNode};
+use crate::html::ImplicitClone;
/// This struct represents a suspendable DOM fragment.
#[derive(Clone, Debug, PartialEq)]
pub struct VSuspense {
/// Child nodes.
- pub(crate) children: Box,
+ pub(crate) children: VNode,
/// Fallback nodes when suspended.
- pub(crate) fallback: Box,
+ pub(crate) fallback: VNode,
/// Whether the current status is suspended.
pub(crate) suspended: bool,
/// The Key.
pub(crate) key: Option,
}
+impl ImplicitClone for VSuspense {}
+
impl VSuspense {
pub fn new(children: VNode, fallback: VNode, suspended: bool, key: Option) -> Self {
Self {
- children: children.into(),
- fallback: fallback.into(),
+ children,
+ fallback,
suspended,
key,
}
diff --git a/packages/yew/src/virtual_dom/vtag.rs b/packages/yew/src/virtual_dom/vtag.rs
index 266c1e8f339..60f70c53c14 100644
--- a/packages/yew/src/virtual_dom/vtag.rs
+++ b/packages/yew/src/virtual_dom/vtag.rs
@@ -1,6 +1,5 @@
//! This module contains the implementation of a virtual element node [VTag].
-use std::borrow::Cow;
use std::cmp::PartialEq;
use std::marker::PhantomData;
use std::mem;
@@ -10,7 +9,7 @@ use std::rc::Rc;
use web_sys::{HtmlInputElement as InputElement, HtmlTextAreaElement as TextAreaElement};
use super::{ApplyAttributeAs, AttrValue, Attributes, Key, Listener, Listeners, VNode};
-use crate::html::{IntoPropValue, NodeRef};
+use crate::html::{ImplicitClone, IntoPropValue, NodeRef};
/// SVG namespace string used for creating svg elements
pub const SVG_NAMESPACE: &str = "http://www.w3.org/2000/svg";
@@ -22,9 +21,17 @@ pub const MATHML_NAMESPACE: &str = "http://www.w3.org/1998/Math/MathML";
pub const HTML_NAMESPACE: &str = "http://www.w3.org/1999/xhtml";
/// Value field corresponding to an [Element]'s `value` property
-#[derive(Clone, Debug, Eq, PartialEq)]
+#[derive(Debug, Eq, PartialEq)]
pub(crate) struct Value(Option, PhantomData);
+impl Clone for Value {
+ fn clone(&self) -> Self {
+ Self::new(self.0.clone())
+ }
+}
+
+impl ImplicitClone for Value {}
+
impl Default for Value {
fn default() -> Self {
Self::new(None)
@@ -68,6 +75,8 @@ pub(crate) struct InputFields {
pub(crate) checked: Option,
}
+impl ImplicitClone for InputFields {}
+
impl Deref for InputFields {
type Target = Value;
@@ -111,12 +120,14 @@ pub(crate) enum VTagInner {
/// Fields for all other kinds of [VTag]s
Other {
/// A tag of the element.
- tag: Cow<'static, str>,
+ tag: AttrValue,
/// children of the element.
children: VNode,
},
}
+impl ImplicitClone for VTagInner {}
+
/// A type for a virtual
/// [Element](https://developer.mozilla.org/en-US/docs/Web/API/Element)
/// representation.
@@ -133,10 +144,12 @@ pub struct VTag {
pub key: Option,
}
+impl ImplicitClone for VTag {}
+
impl VTag {
/// Creates a new [VTag] instance with `tag` name (cannot be changed later in DOM).
- pub fn new(tag: impl Into>) -> Self {
- let tag: Cow<'static, str> = tag.into();
+ pub fn new(tag: impl Into) -> Self {
+ let tag = tag.into();
Self::new_base(
match &*tag.to_ascii_lowercase() {
"input" => VTagInner::Input(Default::default()),
@@ -226,7 +239,7 @@ impl VTag {
#[doc(hidden)]
#[allow(clippy::too_many_arguments)]
pub fn __new_other(
- tag: Cow<'static, str>,
+ tag: AttrValue,
node_ref: NodeRef,
key: Option,
// at bottom for more readable macro-expanded coded
diff --git a/packages/yew/src/virtual_dom/vtext.rs b/packages/yew/src/virtual_dom/vtext.rs
index beb89acdad7..c1a3d5f38c0 100644
--- a/packages/yew/src/virtual_dom/vtext.rs
+++ b/packages/yew/src/virtual_dom/vtext.rs
@@ -3,6 +3,7 @@
use std::cmp::PartialEq;
use super::AttrValue;
+use crate::html::ImplicitClone;
/// A type for a virtual
/// [`TextNode`](https://developer.mozilla.org/en-US/docs/Web/API/Document/createTextNode)
@@ -13,6 +14,8 @@ pub struct VText {
pub text: AttrValue,
}
+impl ImplicitClone for VText {}
+
impl VText {
/// Creates new virtual text node with a content.
pub fn new(text: impl Into) -> Self {