fix: correctly rebuild reactive attributes to avoid stale signals

This commit is contained in:
Greg Johnston 2024-06-21 09:23:16 -04:00
parent 2a236e043a
commit 309a3d504a
3 changed files with 146 additions and 44 deletions

View file

@ -1,4 +1,4 @@
use super::{ReactiveFunction, RenderEffectState, SharedReactiveFunction};
use super::{ReactiveFunction, RenderEffect, SharedReactiveFunction};
use crate::{html::class::IntoClass, renderer::DomRenderer};
use reactive_graph::{effect::RenderEffect, signal::guards::ReadGuard};
use std::{
@ -14,7 +14,7 @@ where
C::State: 'static,
R: DomRenderer,
{
type State = RenderEffectState<C::State>;
type State = RenderEffect<C::State>;
type Cloneable = SharedReactiveFunction<C>;
type CloneableOwned = SharedReactiveFunction<C>;
@ -60,9 +60,7 @@ where
}
fn rebuild(mut self, state: &mut Self::State) {
let prev_effect = std::mem::take(&mut state.0);
let prev_value = prev_effect.as_ref().and_then(|e| e.take_value());
drop(prev_effect);
let prev_value = state.take_value();
*state = RenderEffect::new_with_value(
move |prev| {
let value = self.invoke();
@ -70,12 +68,11 @@ where
value.rebuild(&mut state);
state
} else {
todo!()
unreachable!()
}
},
prev_value,
)
.into();
);
}
fn into_cloneable(self) -> Self::Cloneable {
@ -93,7 +90,7 @@ where
T: Borrow<bool> + 'static,
R: DomRenderer,
{
type State = RenderEffectState<(R::ClassList, bool)>;
type State = RenderEffect<(R::ClassList, bool)>;
type Cloneable = (&'static str, SharedReactiveFunction<T>);
type CloneableOwned = (&'static str, SharedReactiveFunction<T>);
@ -159,8 +156,30 @@ where
.into()
}
fn rebuild(self, _state: &mut Self::State) {
// TODO rebuild?
fn rebuild(self, state: &mut Self::State) {
let (name, mut f) = self;
let prev_value = state.take_value();
*state = RenderEffect::new_with_value(
move |prev| {
let include = *f.invoke().borrow();
match prev {
Some((class_list, prev)) => {
if include {
if !prev {
R::add_class(&class_list, name);
}
} else if prev {
R::remove_class(&class_list, name);
}
(class_list.clone(), include)
}
None => {
unreachable!()
}
}
},
prev_value,
);
}
fn into_cloneable(self) -> Self::Cloneable {
@ -178,7 +197,7 @@ where
T: Borrow<bool> + 'static,
R: DomRenderer,
{
type State = RenderEffectState<(R::ClassList, bool)>;
type State = RenderEffect<(R::ClassList, bool)>;
type Cloneable = (Vec<Cow<'static, str>>, SharedReactiveFunction<T>);
type CloneableOwned = (Vec<Cow<'static, str>>, SharedReactiveFunction<T>);
@ -255,8 +274,35 @@ where
.into()
}
fn rebuild(self, _state: &mut Self::State) {
// TODO rebuild?
fn rebuild(self, state: &mut Self::State) {
let (names, mut f) = self;
let prev_value = state.take_value();
*state = RenderEffect::new_with_value(
move |prev: Option<(R::ClassList, bool)>| {
let include = *f.invoke().borrow();
match prev {
Some((class_list, prev)) => {
if include {
for name in &names {
if !prev {
R::add_class(&class_list, name);
}
}
} else if prev {
for name in &names {
R::remove_class(&class_list, name);
}
}
(class_list.clone(), include)
}
None => {
unreachable!()
}
}
},
prev_value,
);
}
fn into_cloneable(self) -> Self::Cloneable {
@ -375,7 +421,7 @@ mod stable {
C::State: 'static,
R: DomRenderer,
{
type State = RenderEffectState<C::State>;
type State = RenderEffect<C::State>;
type Cloneable = Self;
type CloneableOwned = Self;
@ -399,8 +445,8 @@ mod stable {
(move || self.get()).build(el)
}
fn rebuild(self, _state: &mut Self::State) {
// TODO rebuild here?
fn rebuild(self, state: &mut Self::State) {
(move || self.get()).rebuild(state)
}
fn into_cloneable(self) -> Self::Cloneable {
@ -416,7 +462,7 @@ mod stable {
where
R: DomRenderer,
{
type State = RenderEffectState<(R::ClassList, bool)>;
type State = RenderEffect<(R::ClassList, bool)>;
type Cloneable = Self;
type CloneableOwned = Self;
@ -446,8 +492,11 @@ mod stable {
IntoClass::<R>::build((self.0, move || self.1.get()), el)
}
fn rebuild(self, _state: &mut Self::State) {
// TODO rebuild here?
fn rebuild(self, state: &mut Self::State) {
IntoClass::<R>::rebuild(
(self.0, move || self.1.get()),
state,
)
}
fn into_cloneable(self) -> Self::Cloneable {
@ -469,7 +518,7 @@ mod stable {
C::State: 'static,
R: DomRenderer,
{
type State = RenderEffectState<C::State>;
type State = RenderEffect<C::State>;
type Cloneable = Self;
type CloneableOwned = Self;
@ -493,8 +542,8 @@ mod stable {
(move || self.get()).build(el)
}
fn rebuild(self, _state: &mut Self::State) {
// TODO rebuild here?
fn rebuild(self, state: &mut Self::State) {
(move || self.get()).rebuild(state)
}
fn into_cloneable(self) -> Self::Cloneable {
@ -510,7 +559,7 @@ mod stable {
where
R: DomRenderer,
{
type State = RenderEffectState<(R::ClassList, bool)>;
type State = RenderEffect<(R::ClassList, bool)>;
type Cloneable = Self;
type CloneableOwned = Self;
@ -540,8 +589,11 @@ mod stable {
IntoClass::<R>::build((self.0, move || self.1.get()), el)
}
fn rebuild(self, _state: &mut Self::State) {
// TODO rebuild here?
fn rebuild(self, state: &mut Self::State) {
IntoClass::<R>::rebuild(
(self.0, move || self.1.get()),
state,
)
}
fn into_cloneable(self) -> Self::Cloneable {
@ -555,7 +607,7 @@ mod stable {
};
}
use super::RenderEffectState;
use super::RenderEffect;
use crate::{html::class::IntoClass, renderer::DomRenderer};
use reactive_graph::{
computed::{ArcMemo, Memo},

View file

@ -57,8 +57,21 @@ where
})
}
fn rebuild(self, _state: &mut Self::State, _key: &str) {
// TODO rebuild
fn rebuild(mut self, state: &mut Self::State, key: &str) {
let prev_value = state.take_value();
let key = key.to_owned();
*state = RenderEffect::new_with_value(
move |prev| {
let value = self.invoke();
if let Some(mut state) = prev {
value.rebuild(&mut state, &key);
state
} else {
unreachable!()
}
},
prev_value,
);
}
fn into_cloneable(self) -> Self::Cloneable {
@ -112,8 +125,8 @@ mod stable {
(move || self.get()).build(el, key)
}
fn rebuild(self, _state: &mut Self::State, _key: &str) {
// TODO rebuild
fn rebuild(self, state: &mut Self::State, key: &str) {
(move || self.get()).rebuild(state, key)
}
fn into_cloneable(self) -> Self::Cloneable {
@ -155,8 +168,8 @@ mod stable {
(move || self.get()).build(el, key)
}
fn rebuild(self, _state: &mut Self::State, _key: &str) {
// TODO rebuild
fn rebuild(self, state: &mut Self::State, key: &str) {
(move || self.get()).rebuild(state, key)
}
fn into_cloneable(self) -> Self::Cloneable {

View file

@ -74,8 +74,25 @@ where
})
}
fn rebuild(self, _state: &mut Self::State) {
// TODO rebuild
fn rebuild(self, state: &mut Self::State) {
let (name, mut f) = self;
let prev_value = state.take_value();
*state = RenderEffect::new_with_value(
move |prev| {
let value = f.invoke().into();
if let Some(mut state) = prev {
let (style, prev) = &mut state;
if &value != prev {
R::set_css_property(style, name, &value);
}
*prev = value;
state
} else {
unreachable!()
}
},
prev_value,
);
}
fn into_cloneable(self) -> Self::Cloneable {
@ -133,7 +150,21 @@ where
})
}
fn rebuild(self, _state: &mut Self::State) {}
fn rebuild(mut self, state: &mut Self::State) {
let prev_value = state.take_value();
*state = RenderEffect::new_with_value(
move |prev| {
let value = self.invoke();
if let Some(mut state) = prev {
value.rebuild(&mut state);
state
} else {
unreachable!()
}
},
prev_value,
);
}
fn into_cloneable(self) -> Self::Cloneable {
self.into_shared()
@ -174,8 +205,8 @@ mod stable {
(move || self.get()).build(el)
}
fn rebuild(self, _state: &mut Self::State) {
// TODO rebuild here?
fn rebuild(self, state: &mut Self::State) {
(move || self.get()).rebuild(el, state)
}
fn into_cloneable(self) -> Self::Cloneable {
@ -218,8 +249,11 @@ mod stable {
IntoStyle::<R>::build((self.0, move || self.1.get()), el)
}
fn rebuild(self, _state: &mut Self::State) {
// TODO rebuild here?
fn rebuild(self, state: &mut Self::State) {
IntoStyle::<R>::rebuild(
(self.0, move || self.1.get()),
state,
)
}
fn into_cloneable(self) -> Self::Cloneable {
@ -261,8 +295,8 @@ mod stable {
(move || self.get()).build(el)
}
fn rebuild(self, _state: &mut Self::State) {
// TODO rebuild here?
fn rebuild(self, state: &mut Self::State) {
(move || self.get()).rebuild(state)
}
fn into_cloneable(self) -> Self::Cloneable {
@ -305,8 +339,11 @@ mod stable {
IntoStyle::<R>::build((self.0, move || self.1.get()), el)
}
fn rebuild(self, _state: &mut Self::State) {
// TODO rebuild here?
fn rebuild(self, state: &mut Self::State) {
IntoStyle::<R>::rebuild(
(self.0, move || self.1.get()),
state,
)
}
fn into_cloneable(self) -> Self::Cloneable {