Introduce immutable string, array and map (#2563)

This commit is contained in:
Cecile Tonglet 2022-06-24 16:58:20 +01:00 committed by GitHub
parent 6b89e21034
commit 7ddf26752f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 357 additions and 205 deletions

View File

@ -36,6 +36,7 @@ members = [
"examples/web_worker_fib",
"examples/ssr_router",
"examples/suspense",
"examples/immutable",
# Tools
"tools/benchmark-struct",

View File

@ -38,6 +38,7 @@ As an example, check out the TodoMVC example here: <https://examples.yew.rs/todo
| [function_todomvc](function_todomvc) | F | Implementation of [TodoMVC](http://todomvc.com/) using function components and hooks. |
| [futures](futures) | S | Demonstrates how you can use futures and async code with Yew. Features a Markdown renderer. |
| [game_of_life](game_of_life) | S | Implementation of [Conway's Game of Life](https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life) |
| [immutable](immutable) | SF | Using immutable types in components. |
| [inner_html](inner_html) | S | Embeds an external document as raw HTML by manually managing the element |
| [js_callback](js_callback) | F | Interacts with JavaScript code |
| [keyed_list](keyed_list) | S | Demonstrates how to use keys to improve the performance of lists |

View File

@ -0,0 +1,12 @@
[package]
name = "immutable"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
implicit-clone = { version = "0.2", features = ["map"] }
wasm-bindgen = "0.2"
web-sys = "0.3"
yew = { path = "../../packages/yew", features = ["csr"] }

View File

@ -0,0 +1,5 @@
# Immutable Example
[![Demo](https://img.shields.io/website?label=demo&url=https%3A%2F%2Fexamples.yew.rs%2Fimmutable)](https://examples.yew.rs/immutable)
This is a technical demonstration for how to use immutables types in Yew.

View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Yew • Immutable</title>
<link data-trunk rel="scss" href="index.scss"/>
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@900&display=swap" rel="stylesheet">
</head>
<body></body>
</html>

View File

@ -0,0 +1,11 @@
$font-stack: Roboto, sans-serif;
$primary-color: #f5f5f5;
body {
font: 100% $font-stack;
color: black;
background-color: $primary-color;
margin: 0 auto;
min-width: 230px;
max-width: 550px;
}

View File

@ -0,0 +1,54 @@
use implicit_clone::unsync::*;
use wasm_bindgen::{JsCast, UnwrapThrowExt};
use web_sys::{HtmlInputElement, KeyboardEvent};
use yew::prelude::*;
#[derive(Properties, PartialEq)]
struct FolksViewProps {
folks: IArray<IString>,
}
#[function_component(FolksView)]
fn folks_view(props: &FolksViewProps) -> Html {
html! {
<>
<p>{"Hello to:"}</p>
<ul>
{ for props.folks.iter().map(|s| html!(<li>{s}</li>)) }
</ul>
</>
}
}
#[function_component(ArrayExample)]
pub fn array_example() -> Html {
let folks = use_state(IArray::<IString>::default);
let onkeyup = {
let folks = folks.clone();
Callback::from(move |e: KeyboardEvent| {
if e.key() == "Enter" {
let event: Event = e.dyn_into().unwrap_throw();
let event_target = event.target().unwrap_throw();
let target: HtmlInputElement = event_target.dyn_into().unwrap_throw();
let name = target.value();
target.set_value("");
folks.set(
folks
.iter()
.chain(std::iter::once(IString::from(name)))
.collect(),
);
}
})
};
html! {
<>
<h2>{"Input"}</h2>
<input {onkeyup} />
<h2>{"Output"}</h2>
<FolksView folks={&*folks} />
</>
}
}

View File

@ -0,0 +1,29 @@
mod array;
mod map;
mod string;
use yew::prelude::*;
use self::array::*;
use self::map::*;
use self::string::*;
#[function_component]
fn App() -> Html {
html! {
<>
<h1>{ "IString Example" }</h1>
<StringExample />
<hr/>
<h1>{ "IArray Example" }</h1>
<ArrayExample />
<hr/>
<h1>{ "IMap Example" }</h1>
<MapExample />
</>
}
}
fn main() {
yew::Renderer::<App>::new().render();
}

View File

@ -0,0 +1,83 @@
use implicit_clone::unsync::*;
use wasm_bindgen::{JsCast, UnwrapThrowExt};
use web_sys::{HtmlInputElement, KeyboardEvent};
use yew::prelude::*;
#[derive(Properties, PartialEq)]
struct DisplayProps {
values: IMap<u32, IString>,
}
#[function_component]
fn Display(props: &DisplayProps) -> Html {
html! {
<>
<p>{"Hello to:"}</p>
<ul>
{ for props.values.iter().map(|(i, s)| html!(<li>{i}{" => "}{s}</li>)) }
</ul>
</>
}
}
pub struct MapExample {
values: IMap<u32, IString>,
}
pub enum MapExampleMessage {
AddName(String),
Noop,
}
impl Component for MapExample {
type Message = MapExampleMessage;
type Properties = ();
fn create(_ctx: &Context<Self>) -> Self {
Self {
values: Default::default(),
}
}
fn update(&mut self, _: &Context<Self>, msg: Self::Message) -> bool {
match msg {
MapExampleMessage::AddName(name) => {
self.values = self
.values
.iter()
.chain(std::iter::once((
self.values.len() as u32,
IString::from(name),
)))
.collect();
true
}
MapExampleMessage::Noop => false,
}
}
fn view(&self, ctx: &Context<Self>) -> Html {
let link = ctx.link();
let onkeyup = link.callback(|e: KeyboardEvent| {
if e.key() == "Enter" {
let event: Event = e.dyn_into().unwrap_throw();
let event_target = event.target().unwrap_throw();
let target: HtmlInputElement = event_target.dyn_into().unwrap_throw();
let value = target.value();
target.set_value("");
MapExampleMessage::AddName(value)
} else {
MapExampleMessage::Noop
}
});
html! {
<>
<h2>{"Input"}</h2>
<input {onkeyup} />
<h2>{"Output"}</h2>
<Display values={&self.values} />
</>
}
}
}

View File

@ -0,0 +1,63 @@
use implicit_clone::unsync::*;
use wasm_bindgen::{JsCast, UnwrapThrowExt};
use web_sys::{HtmlInputElement, InputEvent};
use yew::prelude::*;
#[derive(Properties, PartialEq)]
struct DisplayProps {
name: IString,
}
#[function_component]
fn Display(props: &DisplayProps) -> Html {
html! {
<p>{"Hello "}{&props.name}{"!"}</p>
}
}
pub struct StringExample {
name: IString,
}
pub enum StringExampleMessage {
UpdateName(String),
}
impl Component for StringExample {
type Message = StringExampleMessage;
type Properties = ();
fn create(_ctx: &Context<Self>) -> Self {
Self {
name: "World".into(),
}
}
fn update(&mut self, _: &Context<Self>, msg: Self::Message) -> bool {
match msg {
StringExampleMessage::UpdateName(name) => {
self.name = name.into();
true
}
}
}
fn view(&self, ctx: &Context<Self>) -> Html {
let link = ctx.link();
let oninput = link.callback(|e: InputEvent| {
let event: Event = e.dyn_into().unwrap_throw();
let event_target = event.target().unwrap_throw();
let target: HtmlInputElement = event_target.dyn_into().unwrap_throw();
StringExampleMessage::UpdateName(target.value())
});
html! {
<>
<h2>{"Input"}</h2>
<input value={&self.name} {oninput} />
<h2>{"Output"}</h2>
<Display name={&self.name} />
</>
}
}
}

View File

@ -345,11 +345,11 @@ error[E0277]: the trait bound `{integer}: IntoPropValue<String>` is not satisfie
| ^ the trait `IntoPropValue<String>` is not implemented for `{integer}`
|
= help: the following implementations were found:
<&'static str as IntoPropValue<AttrValue>>
<&'static [(K, V)] as IntoPropValue<implicit_clone::unsync::IMap<K, V>>>
<&'static [T] as IntoPropValue<implicit_clone::unsync::IArray<T>>>
<&'static str as IntoPropValue<Classes>>
<&'static str as IntoPropValue<Option<AttrValue>>>
<&'static str as IntoPropValue<Option<String>>>
and 27 others
and 31 others
error[E0277]: the trait bound `{integer}: IntoPropValue<String>` is not satisfied
--> tests/html_macro/component-fail.rs:79:34
@ -358,11 +358,11 @@ error[E0277]: the trait bound `{integer}: IntoPropValue<String>` is not satisfie
| ^ the trait `IntoPropValue<String>` is not implemented for `{integer}`
|
= help: the following implementations were found:
<&'static str as IntoPropValue<AttrValue>>
<&'static [(K, V)] as IntoPropValue<implicit_clone::unsync::IMap<K, V>>>
<&'static [T] as IntoPropValue<implicit_clone::unsync::IArray<T>>>
<&'static str as IntoPropValue<Classes>>
<&'static str as IntoPropValue<Option<AttrValue>>>
<&'static str as IntoPropValue<Option<String>>>
and 27 others
and 31 others
error[E0308]: mismatched types
--> tests/html_macro/component-fail.rs:80:31

View File

@ -246,11 +246,11 @@ error[E0308]: mismatched types
40 | html! { <option selected=1 /> };
| ^ expected `bool`, found integer
error[E0277]: the trait bound `(): IntoPropValue<Option<AttrValue>>` is not satisfied
error[E0277]: the trait bound `(): IntoPropValue<Option<implicit_clone::unsync::IString>>` is not satisfied
--> tests/html_macro/element-fail.rs:43:26
|
43 | html! { <input type={()} /> };
| ^^ the trait `IntoPropValue<Option<AttrValue>>` is not implemented for `()`
| ^^ the trait `IntoPropValue<Option<implicit_clone::unsync::IString>>` is not implemented for `()`
|
note: required by `into_prop_value`
--> $WORKSPACE/packages/yew/src/html/conversion.rs
@ -258,11 +258,11 @@ note: required by `into_prop_value`
| fn into_prop_value(self) -> T;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0277]: the trait bound `(): IntoPropValue<Option<AttrValue>>` is not satisfied
error[E0277]: the trait bound `(): IntoPropValue<Option<implicit_clone::unsync::IString>>` is not satisfied
--> tests/html_macro/element-fail.rs:44:27
|
44 | html! { <input value={()} /> };
| ^^ the trait `IntoPropValue<Option<AttrValue>>` is not implemented for `()`
| ^^ the trait `IntoPropValue<Option<implicit_clone::unsync::IString>>` is not implemented for `()`
|
note: required by `into_prop_value`
--> $WORKSPACE/packages/yew/src/html/conversion.rs
@ -270,11 +270,11 @@ note: required by `into_prop_value`
| fn into_prop_value(self) -> T;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0277]: the trait bound `(): IntoPropValue<Option<AttrValue>>` is not satisfied
error[E0277]: the trait bound `(): IntoPropValue<Option<implicit_clone::unsync::IString>>` is not satisfied
--> tests/html_macro/element-fail.rs:45:22
|
45 | html! { <a href={()} /> };
| ^^ the trait `IntoPropValue<Option<AttrValue>>` is not implemented for `()`
| ^^ the trait `IntoPropValue<Option<implicit_clone::unsync::IString>>` is not implemented for `()`
|
note: required by `into_prop_value`
--> $WORKSPACE/packages/yew/src/html/conversion.rs
@ -282,11 +282,11 @@ note: required by `into_prop_value`
| fn into_prop_value(self) -> T;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0277]: the trait bound `NotToString: IntoPropValue<Option<AttrValue>>` is not satisfied
error[E0277]: the trait bound `NotToString: IntoPropValue<Option<implicit_clone::unsync::IString>>` is not satisfied
--> tests/html_macro/element-fail.rs:46:28
|
46 | html! { <input string={NotToString} /> };
| ^^^^^^^^^^^ the trait `IntoPropValue<Option<AttrValue>>` is not implemented for `NotToString`
| ^^^^^^^^^^^ the trait `IntoPropValue<Option<implicit_clone::unsync::IString>>` is not implemented for `NotToString`
|
note: required by `into_prop_value`
--> $WORKSPACE/packages/yew/src/html/conversion.rs
@ -294,17 +294,17 @@ note: required by `into_prop_value`
| fn into_prop_value(self) -> T;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0277]: the trait bound `Option<NotToString>: IntoPropValue<Option<AttrValue>>` is not satisfied
error[E0277]: the trait bound `Option<NotToString>: IntoPropValue<Option<implicit_clone::unsync::IString>>` is not satisfied
--> tests/html_macro/element-fail.rs:47:23
|
47 | html! { <a media={Some(NotToString)} /> };
| ^^^^^^^^^^^^^^^^^ the trait `IntoPropValue<Option<AttrValue>>` is not implemented for `Option<NotToString>`
| ^^^^^^^^^^^^^^^^^ the trait `IntoPropValue<Option<implicit_clone::unsync::IString>>` is not implemented for `Option<NotToString>`
|
= help: the following implementations were found:
<Option<&'static str> as IntoPropValue<Option<AttrValue>>>
<Option<&'static str> as IntoPropValue<Option<String>>>
<Option<&'static str> as IntoPropValue<Option<implicit_clone::unsync::IString>>>
<Option<F> as IntoPropValue<Option<yew::Callback<I, O>>>>
<Option<String> as IntoPropValue<Option<AttrValue>>>
<Option<Rc<str>> as IntoPropValue<Option<implicit_clone::unsync::IString>>>
and 4 others
note: required by `into_prop_value`
--> $WORKSPACE/packages/yew/src/html/conversion.rs
@ -312,17 +312,17 @@ note: required by `into_prop_value`
| fn into_prop_value(self) -> T;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0277]: the trait bound `Option<{integer}>: IntoPropValue<Option<AttrValue>>` is not satisfied
error[E0277]: the trait bound `Option<{integer}>: IntoPropValue<Option<implicit_clone::unsync::IString>>` is not satisfied
--> tests/html_macro/element-fail.rs:48:22
|
48 | html! { <a href={Some(5)} /> };
| ^^^^^^^ the trait `IntoPropValue<Option<AttrValue>>` is not implemented for `Option<{integer}>`
| ^^^^^^^ the trait `IntoPropValue<Option<implicit_clone::unsync::IString>>` is not implemented for `Option<{integer}>`
|
= help: the following implementations were found:
<Option<&'static str> as IntoPropValue<Option<AttrValue>>>
<Option<&'static str> as IntoPropValue<Option<String>>>
<Option<&'static str> as IntoPropValue<Option<implicit_clone::unsync::IString>>>
<Option<F> as IntoPropValue<Option<yew::Callback<I, O>>>>
<Option<String> as IntoPropValue<Option<AttrValue>>>
<Option<Rc<str>> as IntoPropValue<Option<implicit_clone::unsync::IString>>>
and 4 others
note: required by `into_prop_value`
--> $WORKSPACE/packages/yew/src/html/conversion.rs
@ -416,10 +416,10 @@ error[E0277]: the trait bound `Option<yew::NodeRef>: IntoPropValue<yew::NodeRef>
| ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `IntoPropValue<yew::NodeRef>` is not implemented for `Option<yew::NodeRef>`
|
= help: the following implementations were found:
<Option<&'static str> as IntoPropValue<Option<AttrValue>>>
<Option<&'static str> as IntoPropValue<Option<String>>>
<Option<&'static str> as IntoPropValue<Option<implicit_clone::unsync::IString>>>
<Option<F> as IntoPropValue<Option<yew::Callback<I, O>>>>
<Option<String> as IntoPropValue<Option<AttrValue>>>
<Option<Rc<str>> as IntoPropValue<Option<implicit_clone::unsync::IString>>>
and 4 others
note: required by `into_prop_value`
--> $WORKSPACE/packages/yew/src/html/conversion.rs
@ -451,11 +451,11 @@ note: required by a bound in `yew::html::onclick::Wrapper::__macro_new`
| |_^ required by this bound in `yew::html::onclick::Wrapper::__macro_new`
= note: this error originates in the macro `impl_action` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0277]: the trait bound `NotToString: IntoPropValue<Option<AttrValue>>` is not satisfied
error[E0277]: the trait bound `NotToString: IntoPropValue<Option<implicit_clone::unsync::IString>>` is not satisfied
--> tests/html_macro/element-fail.rs:60:28
|
60 | html! { <input string={NotToString} /> };
| ^^^^^^^^^^^ the trait `IntoPropValue<Option<AttrValue>>` is not implemented for `NotToString`
| ^^^^^^^^^^^ the trait `IntoPropValue<Option<implicit_clone::unsync::IString>>` is not implemented for `NotToString`
|
note: required by `into_prop_value`
--> $WORKSPACE/packages/yew/src/html/conversion.rs

View File

@ -29,6 +29,7 @@ thiserror = "1.0"
futures = { version = "0.3", optional = true }
html-escape = { version = "0.2.9", optional = true }
implicit-clone = { version = "0.2", features = ["map"] }
base64ct = { version = "1.5.0", features = ["std"], optional = true }
bincode = { version = "1.3.3", optional = true }
serde = { version = "1", features = ["derive"] }

View File

@ -1,33 +1,16 @@
use std::rc::Rc;
use implicit_clone::unsync::{IArray, IMap};
pub use implicit_clone::ImplicitClone;
use super::super::callback::Callback;
use super::{BaseComponent, Children, ChildrenRenderer, Component, NodeRef, Scope};
use crate::virtual_dom::{AttrValue, VChild, VNode};
/// Marker trait for types that the [`html!`](macro@crate::html) macro may clone implicitly.
pub trait ImplicitClone: Clone {}
impl<T: ImplicitClone> ImplicitClone for Option<T> {}
impl<T: ?Sized> ImplicitClone for Rc<T> {}
impl ImplicitClone for NodeRef {}
impl<Comp: Component> ImplicitClone for Scope<Comp> {}
// TODO there are still a few missing
macro_rules! impl_implicit_clone {
($($ty:ty),+ $(,)?) => {
$(impl ImplicitClone for $ty {})*
};
}
#[rustfmt::skip]
impl_implicit_clone!(
u8, u16, u32, u64, u128,
i8, i16, i32, i64, i128,
f32, f64,
&'static str,
);
/// A trait similar to `Into<T>` which allows conversion to a value of a `Properties` struct.
pub trait IntoPropValue<T> {
/// Convert `self` to a value of a `Properties` struct.
@ -194,6 +177,34 @@ impl_into_prop!(|value: String| -> AttrValue { AttrValue::Rc(Rc::from(value)) })
impl_into_prop!(|value: Rc<str>| -> AttrValue { AttrValue::Rc(value) });
impl_into_prop!(|value: VNode| -> Children { Children::new(vec![value]) });
impl<T: ImplicitClone + 'static> IntoPropValue<IArray<T>> for &'static [T] {
fn into_prop_value(self) -> IArray<T> {
IArray::from(self)
}
}
impl<T: ImplicitClone + 'static> IntoPropValue<IArray<T>> for Vec<T> {
fn into_prop_value(self) -> IArray<T> {
IArray::from(self)
}
}
impl<K: Eq + std::hash::Hash + ImplicitClone + 'static, V: PartialEq + ImplicitClone + 'static>
IntoPropValue<IMap<K, V>> for &'static [(K, V)]
{
fn into_prop_value(self) -> IMap<K, V> {
IMap::from(self)
}
}
impl<K: Eq + std::hash::Hash + ImplicitClone + 'static, V: PartialEq + ImplicitClone + 'static>
IntoPropValue<IMap<K, V>> for indexmap::IndexMap<K, V>
{
fn into_prop_value(self) -> IMap<K, V> {
IMap::from(self)
}
}
#[cfg(test)]
mod test {
use super::*;

View File

@ -19,13 +19,7 @@ pub mod vtag;
#[doc(hidden)]
pub mod vtext;
use std::borrow::{Borrow, Cow};
use std::fmt;
use std::fmt::Formatter;
use std::hash::{Hash, Hasher};
use std::hint::unreachable_unchecked;
use std::ops::Deref;
use std::rc::Rc;
use indexmap::IndexMap;
@ -49,158 +43,7 @@ pub use self::vtag::VTag;
pub use self::vtext::VText;
/// Attribute value
#[derive(Debug)]
pub enum AttrValue {
/// String living for `'static`
Static(&'static str),
/// Reference counted string
Rc(Rc<str>),
}
impl Deref for AttrValue {
type Target = str;
#[inline(always)]
fn deref(&self) -> &Self::Target {
match self {
AttrValue::Static(s) => *s,
AttrValue::Rc(s) => &*s,
}
}
}
impl Hash for AttrValue {
#[inline(always)]
fn hash<H: Hasher>(&self, state: &mut H) {
self.as_ref().hash(state);
}
}
impl From<&'static str> for AttrValue {
#[inline(always)]
fn from(s: &'static str) -> Self {
AttrValue::Static(s)
}
}
impl From<String> for AttrValue {
#[inline(always)]
fn from(s: String) -> Self {
AttrValue::Rc(Rc::from(s))
}
}
impl From<Rc<str>> for AttrValue {
#[inline(always)]
fn from(s: Rc<str>) -> Self {
AttrValue::Rc(s)
}
}
impl From<Cow<'static, str>> for AttrValue {
#[inline(always)]
fn from(s: Cow<'static, str>) -> Self {
match s {
Cow::Borrowed(s) => s.into(),
Cow::Owned(s) => s.into(),
}
}
}
impl Clone for AttrValue {
fn clone(&self) -> Self {
match self {
AttrValue::Static(s) => AttrValue::Static(s),
AttrValue::Rc(s) => AttrValue::Rc(Rc::clone(s)),
}
}
}
impl AsRef<str> for AttrValue {
#[inline(always)]
fn as_ref(&self) -> &str {
&*self
}
}
impl Borrow<str> for AttrValue {
fn borrow(&self) -> &str {
&*self
}
}
impl fmt::Display for AttrValue {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
AttrValue::Static(s) => write!(f, "{}", s),
AttrValue::Rc(s) => write!(f, "{}", s),
}
}
}
impl PartialEq for AttrValue {
#[inline(always)]
fn eq(&self, other: &Self) -> bool {
self.as_ref() == other.as_ref()
}
}
impl Eq for AttrValue {}
impl AttrValue {
/// Consumes the AttrValue and returns the owned String from the AttrValue whenever possible.
/// For AttrValue::Rc the <str> is cloned to String in case there are other Rc or Weak pointers
/// to the same allocation.
pub fn into_string(self) -> String {
match self {
AttrValue::Static(s) => (*s).to_owned(),
AttrValue::Rc(mut rc) => {
if let Some(s) = Rc::get_mut(&mut rc) {
(*s).to_owned()
} else {
rc.to_string()
}
}
}
}
}
#[cfg(test)]
mod tests_attr_value {
use super::*;
#[test]
fn test_into_string() {
let av = AttrValue::Static("str");
assert_eq!(av.into_string(), "str");
let av = AttrValue::Rc("Rc<str>".into());
assert_eq!(av.into_string(), "Rc<str>");
}
#[test]
fn test_from_string() {
let av = AttrValue::from("str");
assert_eq!(av.into_string(), "str");
let av = AttrValue::from("String".to_string());
assert_eq!(av.into_string(), "String");
let av = AttrValue::from(Cow::from("BorrowedCow"));
assert_eq!(av.into_string(), "BorrowedCow");
}
#[test]
fn test_equality() {
// construct 3 AttrValue with same embedded value; expectation is that all are equal
let a = AttrValue::Static("same");
let b = AttrValue::Rc("same".into());
assert_eq!(a, b);
assert_eq!(a, b);
}
}
pub type AttrValue = implicit_clone::unsync::IString;
#[cfg(any(feature = "ssr", feature = "hydration"))]
mod feat_ssr_hydration {
@ -278,7 +121,7 @@ mod feat_ssr_hydration {
}
#[cfg(feature = "hydration")]
pub fn name(&self) -> super::Cow<'static, str> {
pub fn name(&self) -> std::borrow::Cow<'static, str> {
match self {
#[cfg(debug_assertions)]
Self::Component(m) => format!("Component({})", m).into(),

View File

@ -0,0 +1,24 @@
---
title: 'Immutable Types'
description: 'Immutable data structures for Yew'
---
## What are immutable types?
These are types that you can instantiate but never mutate the values. In order
to update a value, you must instantiate a new value.
## Why using immutable types?
Properties, like in React, are propagated from ancestors to
children. This means that the properties must live when each component is
updated. This is why properties should —ideally— be cheap to clone. In order to
achieve this we usually wrap things in `Rc`.
Immutable types are a great fit for holding property's values because they can
be cheaply cloned when passed from component to component.
## Further reading
- [Immutable example](https://github.com/yewstack/yew/tree/master/examples/immutable)
- [Crate `implicit-clone`](https://docs.rs/implicit-clone/)

View File

@ -129,6 +129,7 @@ module.exports = {
'advanced-topics/optimizations',
'advanced-topics/portals',
'advanced-topics/server-side-rendering',
'advanced-topics/immutable',
],
},
{