Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cleanup ImplicitClone related stuff in the examples #3508

Open
wants to merge 30 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
32df544
Use cheap-to-clone types more
cecton Nov 1, 2023
fe3636d
Use IArray in NodeSeq & add IntoPropValue for &AttrValue
cecton Nov 1, 2023
d0cfdcb
Add function get_mut() on VChild to make things easier
cecton Nov 1, 2023
77b2656
Use AttrValue in timer_functional example
cecton Nov 1, 2023
71bcd09
Allow iterating over ref of ChildrenRenderer
cecton Nov 1, 2023
2401701
Revert "Allow iterating over ref of ChildrenRenderer"
cecton Nov 1, 2023
12aa26d
Still allow ref on childrenrenderer but do not use IArray for now
cecton Nov 1, 2023
46e12dc
rustfmt nightly
cecton Nov 1, 2023
ada85f4
Merge commit 8d2cfdee69fa69625d36ca784a0644e5dd0d5334 (no conflict)
cecton Nov 5, 2023
863f174
Revert changes on NodeSeq because I'm not sure it's useful at all
cecton Nov 6, 2023
8242f53
Improve example, less clone() necessary
cecton Nov 6, 2023
fc402b1
Optimize VList creation from ref ChildrenRenderer
cecton Nov 6, 2023
a9f9f93
Very unfortunate consequence of recent refactoring on implicit-clone
cecton Nov 6, 2023
21f04ef
The Vecs are actually of identical types...
cecton Nov 6, 2023
ba0b64f
Use Rc<Vec<VNode>> in ChildrenRenderer to avoid needless clones
cecton Nov 6, 2023
3b0eac8
Avoid unnecessary allocation
cecton Nov 6, 2023
13676be
Fix invalid use of as_ref()
cecton Nov 6, 2023
47e9651
Oh actually it was useful
cecton Nov 6, 2023
a80d1d4
Fix: NodeSeq now requires OUT to implement ImplicitClone
cecton Nov 6, 2023
ab1e138
Revert unnecessary change
cecton Nov 6, 2023
38d45d9
Simplify code, restore original with_children() signature, optimize
cecton Nov 6, 2023
aa788c3
clippy
cecton Nov 6, 2023
bc96cc7
oops
cecton Nov 6, 2023
0a9a3bc
Less allocations probably
cecton Nov 6, 2023
d7e4acf
Code duplication
cecton Nov 6, 2023
25f3494
Trigger CI
cecton Nov 6, 2023
64c4b63
Upgrade implicit-clone to 0.4.7 and use derive macro when possible
cecton Nov 11, 2023
a5e90d0
Merge commit 1d889347a6205a2dc79c1d1fea9d49092c0627db (no conflict)
cecton Nov 11, 2023
b9baa29
Fix bad location for symbol in test
cecton Nov 11, 2023
224e08b
Update implicit-clone to 0.4.8
cecton Nov 19, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions examples/nested_list/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ edition = "2021"
license = "MIT OR Apache-2.0"

[dependencies]
implicit-clone = "0.4"
log = "0.4"
wasm-logger = "0.2"
yew = { path = "../../packages/yew", features = ["csr"] }
15 changes: 7 additions & 8 deletions examples/nested_list/src/list.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use std::rc::Rc;

use implicit_clone::unsync::IArray;
use yew::prelude::*;
use yew::virtual_dom::VChild;

Expand All @@ -14,9 +13,9 @@ pub enum Msg {
#[derive(Clone, PartialEq, Properties)]
pub struct Props {
#[prop_or_default]
pub header: Vec<VChild<ListHeader>>,
pub header: IArray<VChild<ListHeader>>,
#[prop_or_default]
pub children: Vec<VChild<ListItem>>,
pub children: IArray<VChild<ListItem>>,

pub on_hover: Callback<Hovered>,
pub weak_link: WeakComponentLink<List>,
Expand Down Expand Up @@ -56,7 +55,7 @@ impl Component for List {
html! {
<div class="list-container" {onmouseover}>
<div class={classes!("list", inactive)}>
{ ctx.props().header.clone() }
{ &ctx.props().header }
<div class="items">
{ Self::view_items(ctx.props().children.clone()) }
</div>
Expand All @@ -67,13 +66,13 @@ impl Component for List {
}

impl List {
fn view_items(children: Vec<VChild<ListItem>>) -> Html {
fn view_items(children: IArray<VChild<ListItem>>) -> Html {
children
.into_iter()
.iter()
.filter(|c| !c.props.hide)
.enumerate()
.map(|(i, mut c)| {
let props = Rc::make_mut(&mut c.props);
let props = c.get_mut();
props.name = format!("#{} - {}", i + 1, props.name).into();
c
})
Expand Down
22 changes: 14 additions & 8 deletions examples/timer/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use gloo::console::{self, Timer};
use gloo::timers::callback::{Interval, Timeout};
use yew::{html, Component, Context, Html};
use yew::prelude::*;

pub enum Msg {
StartTimeout,
Expand All @@ -13,7 +13,7 @@ pub enum Msg {

pub struct App {
time: String,
messages: Vec<&'static str>,
messages: Vec<AttrValue>,
_standalone: (Interval, Interval),
interval: Option<Interval>,
timeout: Option<Timeout>,
Expand Down Expand Up @@ -68,7 +68,7 @@ impl Component for App {
self.messages.clear();
console::clear!();

self.messages.push("Timer started!");
self.log("Timer started!");
self.console_timer = Some(Timer::new("Timer"));
true
}
Expand All @@ -82,18 +82,18 @@ impl Component for App {
self.messages.clear();
console::clear!();

self.messages.push("Interval started!");
self.log("Interval started!");
true
}
Msg::Cancel => {
self.cancel();
self.messages.push("Canceled!");
self.log("Canceled!");
console::warn!("Canceled!");
true
}
Msg::Done => {
self.cancel();
self.messages.push("Done!");
self.log("Done!");

// todo weblog
// ConsoleService::group();
Expand All @@ -107,7 +107,7 @@ impl Component for App {
true
}
Msg::Tick => {
self.messages.push("Tick...");
self.log("Tick...");
// todo weblog
// ConsoleService::count_named("Tick");
true
Expand Down Expand Up @@ -139,14 +139,20 @@ impl Component for App {
{ &self.time }
</div>
<div id="messages">
{ for self.messages.iter().map(|message| html! { <p>{ *message }</p> }) }
{ for self.messages.iter().map(|message| html! { <p>{ message }</p> }) }
</div>
</div>
</>
}
}
}

impl App {
fn log(&mut self, message: impl Into<AttrValue>) {
self.messages.push(message.into());
}
}

fn main() {
yew::Renderer::<App>::new().render();
}
39 changes: 31 additions & 8 deletions examples/timer_functional/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,34 @@ enum TimerAction {

#[derive(Clone, Debug)]
struct TimerState {
messages: Vec<&'static str>,
messages: Messages,
interval_handle: Option<Rc<Interval>>,
timeout_handle: Option<Rc<Timeout>>,
}

#[derive(Clone, Debug, Default, PartialEq, Eq)]
struct Messages(Vec<AttrValue>);

impl Messages {
fn log(&mut self, message: impl Into<AttrValue>) {
self.0.push(message.into());
}
}

impl std::ops::Deref for Messages {
type Target = Vec<AttrValue>;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl FromIterator<&'static str> for Messages {
fn from_iter<T: IntoIterator<Item = &'static str>>(it: T) -> Self {
Messages(it.into_iter().map(Into::into).collect())
}
}

impl PartialEq for TimerState {
fn eq(&self, other: &Self) -> bool {
self.messages == other.messages
Expand All @@ -37,26 +60,26 @@ impl Reducible for TimerState {
match action {
TimerAction::Add(message) => {
let mut messages = self.messages.clone();
messages.push(message);
messages.log(message);
Rc::new(TimerState {
messages,
interval_handle: self.interval_handle.clone(),
timeout_handle: self.timeout_handle.clone(),
})
}
TimerAction::SetInterval(t) => Rc::new(TimerState {
messages: vec!["Interval started!"],
messages: ["Interval started!"].into_iter().collect(),
interval_handle: Some(Rc::from(t)),
timeout_handle: self.timeout_handle.clone(),
}),
TimerAction::SetTimeout(t) => Rc::new(TimerState {
messages: vec!["Timer started!!"],
messages: ["Timer started!!"].into_iter().collect(),
interval_handle: self.interval_handle.clone(),
timeout_handle: Some(Rc::from(t)),
}),
TimerAction::TimeoutDone => {
let mut messages = self.messages.clone();
messages.push("Done!");
messages.log("Done!");
Rc::new(TimerState {
messages,
interval_handle: self.interval_handle.clone(),
Expand All @@ -65,7 +88,7 @@ impl Reducible for TimerState {
}
TimerAction::Cancel => {
let mut messages = self.messages.clone();
messages.push("Canceled!");
messages.log("Canceled!");
Rc::new(TimerState {
messages,
interval_handle: None,
Expand Down Expand Up @@ -94,7 +117,7 @@ fn clock() -> Html {
#[function_component]
fn App() -> Html {
let state = use_reducer(|| TimerState {
messages: Vec::new(),
messages: Default::default(),
interval_handle: None,
timeout_handle: None,
});
Expand All @@ -105,7 +128,7 @@ fn App() -> Html {
.iter()
.map(|message| {
key += 1;
html! { <p key={ key }>{ *message }</p> }
html! { <p {key}>{ message }</p> }
})
.collect();

Expand Down
2 changes: 1 addition & 1 deletion packages/yew/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ yew-macro = { version = "^0.21.0", path = "../yew-macro" }
thiserror = "1.0"
futures = { version = "0.3", default-features = false, features = ["std"] }
html-escape = { version = "0.2.13", optional = true }
implicit-clone = { version = "0.4.1", features = ["map"] }
implicit-clone = { version = "0.4.5", features = ["map"] }
base64ct = { version = "1.6.0", features = ["std"], optional = true }
bincode = { version = "1.3.3", optional = true }
serde = { version = "1", features = ["derive"] }
Expand Down
2 changes: 1 addition & 1 deletion packages/yew/src/html/component/children.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ where
/// children.map(|children| {
/// html! {
/// <div class={classes!("container")}>
/// {children.clone()}
/// {children}
/// </div>
/// }
/// })
Expand Down
8 changes: 8 additions & 0 deletions packages/yew/src/html/conversion/into_prop_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,13 @@ impl IntoPropValue<VNode> for ChildrenRenderer<VNode> {
}
}

impl IntoPropValue<VNode> for &ChildrenRenderer<VNode> {
#[inline]
fn into_prop_value(self) -> VNode {
VNode::VList(Rc::new(self.clone().into()))
}
}

impl IntoPropValue<ChildrenRenderer<VNode>> for VNode {
#[inline]
fn into_prop_value(self) -> ChildrenRenderer<VNode> {
Expand Down Expand Up @@ -326,6 +333,7 @@ impl_into_prop_value_via_display!(f64);

impl_into_prop_value_via_attr_value!(String);
impl_into_prop_value_via_attr_value!(AttrValue);
impl_into_prop_value_via_attr_value!(&AttrValue);
impl_into_prop_value_via_attr_value!(Rc<str>);
impl_into_prop_value_via_attr_value!(Cow<'static, str>);

Expand Down
52 changes: 40 additions & 12 deletions packages/yew/src/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

use std::marker::PhantomData;

use implicit_clone::unsync::IArray;
use implicit_clone::ImplicitClone;
use yew::html::ChildrenRenderer;

/// Map `IntoIterator<Item = Into<T>>` to `Iterator<Item = T>`
Expand All @@ -15,38 +17,64 @@ where

/// A special type necessary for flattening components returned from nested html macros.
#[derive(Debug)]
pub struct NodeSeq<IN, OUT>(Vec<OUT>, PhantomData<IN>);
pub struct NodeSeq<IN, OUT: ImplicitClone + 'static>(IArray<OUT>, PhantomData<IN>);

impl<IN: Into<OUT>, OUT> From<IN> for NodeSeq<IN, OUT> {
impl<IN: Into<OUT>, OUT: ImplicitClone + 'static> From<IN> for NodeSeq<IN, OUT> {
fn from(val: IN) -> Self {
Self(vec![val.into()], PhantomData)
Self(IArray::Single([val.into()]), PhantomData)
}
}

impl<IN: Into<OUT>, OUT> From<Option<IN>> for NodeSeq<IN, OUT> {
impl<IN: Into<OUT>, OUT: ImplicitClone + 'static> From<Option<IN>> for NodeSeq<IN, OUT> {
fn from(val: Option<IN>) -> Self {
Self(val.map(|s| vec![s.into()]).unwrap_or_default(), PhantomData)
Self(
val.map(|s| IArray::Single([s.into()])).unwrap_or_default(),
PhantomData,
)
}
}

impl<IN: Into<OUT>, OUT> From<Vec<IN>> for NodeSeq<IN, OUT> {
fn from(val: Vec<IN>) -> Self {
Self(val.into_iter().map(|x| x.into()).collect(), PhantomData)
impl<IN: Into<OUT>, OUT: ImplicitClone + 'static> From<Vec<IN>> for NodeSeq<IN, OUT> {
fn from(mut val: Vec<IN>) -> Self {
if val.len() == 1 {
let item = val.pop().unwrap();
Self(IArray::Single([item.into()]), PhantomData)
} else {
Self(val.into_iter().map(|x| x.into()).collect(), PhantomData)
}
}
}

impl<IN: Into<OUT> + Clone, OUT> From<&ChildrenRenderer<IN>> for NodeSeq<IN, OUT> {
impl<IN: Into<OUT> + ImplicitClone, OUT: ImplicitClone + 'static> From<IArray<IN>>
for NodeSeq<IN, OUT>
{
fn from(val: IArray<IN>) -> Self {
Self(val.iter().map(|x| x.into()).collect(), PhantomData)
}
}

impl<IN: Into<OUT> + ImplicitClone, OUT: ImplicitClone + 'static> From<&IArray<IN>>
for NodeSeq<IN, OUT>
{
fn from(val: &IArray<IN>) -> Self {
Self(val.iter().map(|x| x.into()).collect(), PhantomData)
}
}

impl<IN: Into<OUT> + Clone, OUT: ImplicitClone + 'static> From<&ChildrenRenderer<IN>>
for NodeSeq<IN, OUT>
{
fn from(val: &ChildrenRenderer<IN>) -> Self {
Self(val.iter().map(|x| x.into()).collect(), PhantomData)
}
}

impl<IN, OUT> IntoIterator for NodeSeq<IN, OUT> {
type IntoIter = std::vec::IntoIter<Self::Item>;
impl<IN, OUT: ImplicitClone + 'static> IntoIterator for NodeSeq<IN, OUT> {
type IntoIter = implicit_clone::unsync::Iter<Self::Item>;
type Item = OUT;

fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
self.0.iter()
}
}

Expand Down
11 changes: 11 additions & 0 deletions packages/yew/src/virtual_dom/vcomp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,17 @@ where
}
}

impl<COMP> VChild<COMP>
where
COMP: BaseComponent,
COMP::Properties: Clone,
{
/// Get a mutable reference to the underlying properties.
pub fn get_mut(&mut self) -> &mut COMP::Properties {
Rc::make_mut(&mut self.props)
}
}

impl<COMP> From<VChild<COMP>> for VComp
where
COMP: BaseComponent,
Expand Down
Loading