store effects in reactive system

This commit is contained in:
Greg Johnston 2024-02-19 08:30:21 -05:00
parent a8adf8eea2
commit 4bb2bc4797
3 changed files with 32 additions and 46 deletions

View File

@ -12,7 +12,6 @@
- ErrorBoundary - ErrorBoundary
- ssr examples - ssr examples
- reactivity - reactivity
- Effects need to be stored (and not mem::forget)
- Signal wrappers - Signal wrappers
- SignalDispose implementations on all Copy types - SignalDispose implementations on all Copy types
- untracked access warnings - untracked access warnings

View File

@ -207,9 +207,7 @@ pub fn TodoMVC() -> impl IntoView {
// this is the main point of effects: to synchronize reactive state // this is the main point of effects: to synchronize reactive state
// with something outside the reactive system (like localStorage) // with something outside the reactive system (like localStorage)
// TODO: should be a stored value instead of leaked Effect::new(move |_| {
std::mem::forget(Effect::new(move |_| {
leptos::tachys::log("saving todos to localStorage...");
if let Ok(Some(storage)) = window().local_storage() { if let Ok(Some(storage)) = window().local_storage() {
let json = serde_json::to_string(&todos) let json = serde_json::to_string(&todos)
.expect("couldn't serialize Todos"); .expect("couldn't serialize Todos");
@ -217,16 +215,14 @@ pub fn TodoMVC() -> impl IntoView {
log::error!("error while trying to set item in localStorage"); log::error!("error while trying to set item in localStorage");
} }
} }
})); });
// focus the main input on load // focus the main input on load
// TODO: should be a stored value instead of leaked Effect::new(move |_| {
std::mem::forget(Effect::new(move |_| {
leptos::tachys::log("focusing...");
if let Some(input) = input_ref.get() { if let Some(input) = input_ref.get() {
let _ = input.focus(); let _ = input.focus();
} }
})); });
view! { view! {
<main> <main>

View File

@ -2,7 +2,7 @@ use crate::{
channel::{channel, Receiver}, channel::{channel, Receiver},
effect::inner::EffectInner, effect::inner::EffectInner,
graph::{AnySubscriber, SourceSet, Subscriber, ToAnySubscriber}, graph::{AnySubscriber, SourceSet, Subscriber, ToAnySubscriber},
owner::Owner, owner::{Owner, StoredValue},
}; };
use any_spawner::Executor; use any_spawner::Executor;
use futures::StreamExt; use futures::StreamExt;
@ -12,21 +12,8 @@ use std::{
sync::{Arc, RwLock}, sync::{Arc, RwLock},
}; };
pub struct Effect<T> pub struct Effect {
where inner: StoredValue<Option<Arc<RwLock<EffectInner>>>>,
T: 'static,
{
value: Arc<RwLock<Option<T>>>,
inner: Arc<RwLock<EffectInner>>,
}
impl<T> Clone for Effect<T> {
fn clone(&self) -> Self {
Self {
value: Arc::clone(&self.value),
inner: Arc::clone(&self.inner),
}
}
} }
fn effect_base() -> (Receiver, Owner, Arc<RwLock<EffectInner>>) { fn effect_base() -> (Receiver, Owner, Arc<RwLock<EffectInner>>) {
@ -46,18 +33,15 @@ fn effect_base() -> (Receiver, Owner, Arc<RwLock<EffectInner>>) {
(rx, owner, inner) (rx, owner, inner)
} }
impl<T> Effect<T> impl Effect {
where pub fn stop(self) {
T: 'static, drop(self.inner.update_value(|inner| inner.take()));
{
pub fn with_value_mut<U>(
&self,
fun: impl FnOnce(&mut T) -> U,
) -> Option<U> {
self.value.write().or_poisoned().as_mut().map(fun)
} }
pub fn new(mut fun: impl FnMut(Option<T>) -> T + 'static) -> Self { pub fn new<T>(mut fun: impl FnMut(Option<T>) -> T + 'static) -> Self
where
T: 'static,
{
let (mut rx, owner, inner) = effect_base(); let (mut rx, owner, inner) = effect_base();
let value = Arc::new(RwLock::new(None)); let value = Arc::new(RwLock::new(None));
@ -79,17 +63,17 @@ where
} }
}); });
Self { value, inner } Self {
inner: StoredValue::new(Some(inner)),
}
} }
}
impl<T> Effect<T> pub fn new_sync<T>(
where
T: Send + Sync + 'static,
{
pub fn new_sync(
mut fun: impl FnMut(Option<T>) -> T + Send + Sync + 'static, mut fun: impl FnMut(Option<T>) -> T + Send + Sync + 'static,
) -> Self { ) -> Self
where
T: Send + Sync + 'static,
{
let (mut rx, owner, inner) = effect_base(); let (mut rx, owner, inner) = effect_base();
let value = Arc::new(RwLock::new(None)); let value = Arc::new(RwLock::new(None));
@ -110,12 +94,19 @@ where
} }
} }
}); });
Self { value, inner } Self {
inner: StoredValue::new(Some(inner)),
}
} }
} }
impl<T> ToAnySubscriber for Effect<T> { impl ToAnySubscriber for Effect {
fn to_any_subscriber(&self) -> AnySubscriber { fn to_any_subscriber(&self) -> AnySubscriber {
self.inner.to_any_subscriber() self.inner
.with_value(|inner| {
inner.as_ref().map(|inner| inner.to_any_subscriber())
})
.flatten()
.expect("tried to subscribe to effect that has been stopped")
} }
} }