diff --git a/examples/todomvc/src/main.rs b/examples/todomvc/src/main.rs index 1da94da8b..cb7b6b6e8 100644 --- a/examples/todomvc/src/main.rs +++ b/examples/todomvc/src/main.rs @@ -52,30 +52,15 @@ pub struct AppState { impl AppState { fn add_todo(&self, title: String) { - self.todos.set( - self.todos - .get() - .as_ref() - .clone() - .into_iter() - .chain(Some(create_rc_signal(Todo { - title, - completed: false, - id: Uuid::new_v4(), - }))) - .collect(), - ) + self.todos.modify().push(create_rc_signal(Todo { + title, + completed: false, + id: Uuid::new_v4(), + })) } fn remove_todo(&self, id: Uuid) { - self.todos.set( - self.todos - .get() - .iter() - .filter(|todo| todo.get().id != id) - .cloned() - .collect(), - ); + self.todos.modify().retain(|todo| todo.get().id != id); } fn todos_left(&self) -> usize { @@ -110,14 +95,7 @@ impl AppState { } fn clear_completed(&self) { - self.todos.set( - self.todos - .get() - .iter() - .filter(|todo| !todo.get().completed) - .cloned() - .collect(), - ); + self.todos.modify().retain(|todo| !todo.get().completed); } } @@ -456,7 +434,7 @@ pub fn Footer(cx: Scope) -> View { } } } else { - View::empty() + view! { cx, } }) } } diff --git a/packages/sycamore-reactive/src/signal.rs b/packages/sycamore-reactive/src/signal.rs index aa2a47057..209646a9b 100644 --- a/packages/sycamore-reactive/src/signal.rs +++ b/packages/sycamore-reactive/src/signal.rs @@ -2,7 +2,7 @@ use std::fmt::{Debug, Display, Formatter}; use std::hash::Hash; -use std::ops::{AddAssign, Deref, DivAssign, MulAssign, SubAssign}; +use std::ops::{AddAssign, Deref, DerefMut, DivAssign, MulAssign, SubAssign}; use crate::effect::EFFECTS; use crate::*; @@ -246,6 +246,49 @@ impl Signal { } } +/// A mutable reference for modifying a [`Signal`]. +/// +/// Construct this using the [`Signal::modify()`] method. +pub struct Modify<'a, T>(Option, &'a Signal); + +impl<'a, T> Deref for Modify<'a, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + self.0.as_ref().unwrap() + } +} +impl<'a, T> DerefMut for Modify<'a, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.0.as_mut().unwrap() + } +} + +/// When the mutable handle is dropped, update the [`Signal`]. +impl Drop for Modify<'_, T> { + fn drop(&mut self) { + self.1.set(self.0.take().unwrap()) + } +} + +impl Signal { + /// Return a mutable handle to make it easier to mutate the inner value. + /// This requires the inner type to implement [`Clone`]. + /// + /// # Example + /// ``` + /// # use sycamore_reactive::*; + /// # create_scope_immediate(|cx| { + /// let state = create_signal(cx, "Hello ".to_string()); + /// state.modify().push_str("World!"); + /// assert_eq!(*state.get(), "Hello World!"); + /// # }); + /// ``` + pub fn modify(&self) -> Modify { + Modify(Some(self.value.borrow().as_ref().clone()), self) + } +} + impl Signal { /// Take the current value out and replace it with the default value. /// @@ -662,4 +705,19 @@ mod tests { assert_eq!(*counter.get(), 5); }); } + + #[test] + fn signal_modify() { + create_scope_immediate(|cx| { + let signal = create_signal(cx, "Hello ".to_string()); + let counter = create_signal(cx, 0); + create_effect(cx, || { + signal.track(); + counter.set(*counter.get_untracked() + 1); + }); + signal.modify().push_str("World!"); + assert_eq!(*signal.get(), "Hello World!"); + assert_eq!(*counter.get(), 2); + }); + } } diff --git a/packages/sycamore-router/Cargo.toml b/packages/sycamore-router/Cargo.toml index fef0709d1..6b14ec607 100644 --- a/packages/sycamore-router/Cargo.toml +++ b/packages/sycamore-router/Cargo.toml @@ -15,7 +15,7 @@ version = "0.8.0-beta.3" [dependencies] sycamore = { path = "../sycamore", version = "0.8.0-beta.3" } -sycamore-router-macro = { path = "../sycamore-router-macro", version = "0.8.0-beta.3" } +sycamore-router-macro = { path = "../sycamore-router-macro", version = "=0.8.0-beta.3" } wasm-bindgen = "0.2.79" [dependencies.web-sys] diff --git a/packages/sycamore/Cargo.toml b/packages/sycamore/Cargo.toml index d55e50cdd..9e7834d79 100644 --- a/packages/sycamore/Cargo.toml +++ b/packages/sycamore/Cargo.toml @@ -19,7 +19,7 @@ js-sys = "0.3.56" once_cell = { version = "1.10.0", optional = true } paste = "1.0.6" sycamore-futures = { path = "../sycamore-futures", version = "0.8.0-beta.3", optional = true } -sycamore-macro = { path = "../sycamore-macro", version = "0.8.0-beta.3" } +sycamore-macro = { path = "../sycamore-macro", version = "=0.8.0-beta.3" } sycamore-reactive = { path = "../sycamore-reactive", version = "0.8.0-beta.3" } wasm-bindgen = "0.2.79" wasm-bindgen-futures = { version = "0.4.29", optional = true }