store effects in reactive system
This commit is contained in:
parent
a8adf8eea2
commit
4bb2bc4797
1
TODO.md
1
TODO.md
|
@ -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
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue