tests: fix Effect doctests not being executed, and test-related issues (#2886)

This commit is contained in:
Álvaro Mondéjar Rubio 2024-09-12 22:43:32 +02:00 committed by GitHub
parent a083b57260
commit 96a1f80daf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 69 additions and 47 deletions

View file

@ -13,7 +13,7 @@ use reactive_graph::{
effect::RenderEffect,
owner::{provide_context, use_context, Owner},
signal::ArcRwSignal,
traits::{Get, Read, Track, With},
traits::{Dispose, Get, Read, Track, With},
};
use slotmap::{DefaultKey, SlotMap};
use tachys::{
@ -286,7 +286,7 @@ where
self.children.dry_resolve();
// check the set of tasks to see if it is empty, now or later
let eff = reactive_graph::effect::RenderEffect::new_isomorphic({
let eff = reactive_graph::effect::Effect::new_isomorphic({
move |_| {
tasks.track();
if tasks.read().is_empty() {
@ -338,7 +338,7 @@ where
}
children = children => {
// clean up the (now useless) effect
drop(eff);
eff.dispose();
Some(OwnedView::new_with_owner(children, owner))
}

View file

@ -163,10 +163,10 @@ where
#[deprecated = "This function is being removed to conform to Rust idioms. \
Please use `Selector::new()` instead."]
pub fn create_selector<T>(
source: impl Fn() -> T + Clone + 'static,
source: impl Fn() -> T + Clone + Send + Sync + 'static,
) -> Selector<T>
where
T: PartialEq + Eq + Clone + std::hash::Hash + 'static,
T: PartialEq + Eq + Send + Sync + Clone + std::hash::Hash + 'static,
{
Selector::new(source)
}
@ -178,11 +178,11 @@ where
#[deprecated = "This function is being removed to conform to Rust idioms. \
Please use `Selector::new_with_fn()` instead."]
pub fn create_selector_with_fn<T>(
source: impl Fn() -> T + Clone + 'static,
source: impl Fn() -> T + Clone + Send + Sync + 'static,
f: impl Fn(&T, &T) -> bool + Send + Sync + Clone + 'static,
) -> Selector<T>
where
T: PartialEq + Eq + Clone + std::hash::Hash + 'static,
T: PartialEq + Eq + Send + Sync + Clone + std::hash::Hash + 'static,
{
Selector::new_with_fn(source, f)
}

View file

@ -30,7 +30,7 @@ use std::{
/// let a = RwSignal::new(0);
/// let is_selected = Selector::new(move || a.get());
/// let total_notifications = StoredValue::new(0);
/// Effect::new({
/// Effect::new_isomorphic({
/// let is_selected = is_selected.clone();
/// move |_| {
/// if is_selected.selected(5) {
@ -55,7 +55,7 @@ use std::{
///
/// # any_spawner::Executor::tick().await;
/// assert_eq!(is_selected.selected(5), false);
/// # });
/// # }).await;
/// # });
/// ```
#[derive(Clone)]
@ -74,17 +74,17 @@ where
impl<T> Selector<T>
where
T: PartialEq + Eq + Clone + Hash + 'static,
T: PartialEq + Send + Sync + Eq + Clone + Hash + 'static,
{
/// Creates a new selector that compares values using [`PartialEq`].
pub fn new(source: impl Fn() -> T + Clone + 'static) -> Self {
pub fn new(source: impl Fn() -> T + Send + Sync + Clone + 'static) -> Self {
Self::new_with_fn(source, PartialEq::eq)
}
/// Creates a new selector that compares values by returning `true` from a comparator function
/// if the values are the same.
pub fn new_with_fn(
source: impl Fn() -> T + Clone + 'static,
source: impl Fn() -> T + Clone + Send + Sync + 'static,
f: impl Fn(&T, &T) -> bool + Send + Sync + Clone + 'static,
) -> Self {
let subs: Arc<RwLock<FxHashMap<T, ArcRwSignal<bool>>>> =
@ -92,7 +92,7 @@ where
let v: Arc<RwLock<Option<T>>> = Default::default();
let f = Arc::new(f) as Arc<dyn Fn(&T, &T) -> bool + Send + Sync>;
let effect = Arc::new(RenderEffect::new({
let effect = Arc::new(RenderEffect::new_isomorphic({
let subs = Arc::clone(&subs);
let f = Arc::clone(&f);
let v = Arc::clone(&v);

View file

@ -43,6 +43,7 @@ use std::{
/// # use reactive_graph::owner::StoredValue;
/// # tokio_test::block_on(async move {
/// # tokio::task::LocalSet::new().run_until(async move {
/// # any_spawner::Executor::init_tokio();
/// let a = RwSignal::new(0);
/// let b = RwSignal::new(0);
///
@ -52,7 +53,9 @@ use std::{
/// println!("Value: {}", a.get());
/// });
///
/// # assert_eq!(a.get(), 0);
/// a.set(1);
/// # assert_eq!(a.get(), 1);
/// // ✅ because it's subscribed to `a`, the effect reruns and prints "Value: 1"
///
/// // ❌ don't use effects to synchronize state within the reactive system
@ -61,7 +64,7 @@ use std::{
/// // and easily lead to problems like infinite loops
/// b.set(a.get() + 1);
/// });
/// # });
/// # }).await;
/// # });
/// ```
/// ## Web-Specific Notes
@ -182,6 +185,7 @@ impl Effect<LocalStorage> {
/// # use reactive_graph::signal::signal;
/// # tokio_test::block_on(async move {
/// # tokio::task::LocalSet::new().run_until(async move {
/// # any_spawner::Executor::init_tokio();
/// #
/// let (num, set_num) = signal(0);
///
@ -192,13 +196,16 @@ impl Effect<LocalStorage> {
/// },
/// false,
/// );
/// # assert_eq!(num.get(), 0);
///
/// set_num.set(1); // > "Number: 1; Prev: Some(0)"
/// # assert_eq!(num.get(), 1);
///
/// effect.stop(); // stop watching
///
/// set_num.set(2); // (nothing happens)
/// # });
/// # assert_eq!(num.get(), 2);
/// # }).await;
/// # });
/// ```
///
@ -210,6 +217,7 @@ impl Effect<LocalStorage> {
/// # use reactive_graph::signal::signal;
/// # tokio_test::block_on(async move {
/// # tokio::task::LocalSet::new().run_until(async move {
/// # any_spawner::Executor::init_tokio();
/// #
/// let (num, set_num) = signal(0);
/// let (cb_num, set_cb_num) = signal(0);
@ -222,12 +230,17 @@ impl Effect<LocalStorage> {
/// false,
/// );
///
/// # assert_eq!(num.get(), 0);
/// set_num.set(1); // > "Number: 1; Cb: 0"
/// # assert_eq!(num.get(), 1);
///
/// # assert_eq!(cb_num.get(), 0);
/// set_cb_num.set(1); // (nothing happens)
/// # assert_eq!(cb_num.get(), 1);
///
/// set_num.set(2); // > "Number: 2; Cb: 1"
/// # });
/// # assert_eq!(num.get(), 2);
/// # }).await;
/// # });
/// ```
///
@ -243,6 +256,7 @@ impl Effect<LocalStorage> {
/// # use reactive_graph::signal::signal;
/// # tokio_test::block_on(async move {
/// # tokio::task::LocalSet::new().run_until(async move {
/// # any_spawner::Executor::init_tokio();
/// #
/// let (num, set_num) = signal(0);
///
@ -254,8 +268,10 @@ impl Effect<LocalStorage> {
/// true,
/// ); // > "Number: 0; Prev: None"
///
/// # assert_eq!(num.get(), 0);
/// set_num.set(1); // > "Number: 1; Prev: Some(0)"
/// # });
/// # assert_eq!(num.get(), 1);
/// # }).await;
/// # });
/// ```
pub fn watch<D, T>(

View file

@ -135,44 +135,50 @@ where
{
/// Creates a render effect that will run whether the `effects` feature is enabled or not.
pub fn new_isomorphic(
mut fun: impl FnMut(Option<T>) -> T + Send + 'static,
fun: impl FnMut(Option<T>) -> T + Send + Sync + 'static,
) -> Self {
let (mut observer, mut rx) = channel();
observer.notify();
fn erased<T: Send + Sync + 'static>(
mut fun: Box<dyn FnMut(Option<T>) -> T + Send + Sync + 'static>,
) -> RenderEffect<T> {
let (observer, mut rx) = channel();
let value = Arc::new(RwLock::new(None::<T>));
let owner = Owner::new();
let inner = Arc::new(RwLock::new(EffectInner {
dirty: false,
observer,
sources: SourceSet::new(),
}));
let value = Arc::new(RwLock::new(None::<T>));
let owner = Owner::new();
let inner = Arc::new(RwLock::new(EffectInner {
dirty: false,
observer,
sources: SourceSet::new(),
}));
let mut first_run = true;
let initial_value = owner
.with(|| inner.to_any_subscriber().with_observer(|| fun(None)));
*value.write().or_poisoned() = Some(initial_value);
Executor::spawn({
let value = Arc::clone(&value);
let subscriber = inner.to_any_subscriber();
Executor::spawn({
let value = Arc::clone(&value);
let subscriber = inner.to_any_subscriber();
async move {
while rx.next().await.is_some() {
if first_run
|| subscriber
async move {
while rx.next().await.is_some() {
if subscriber
.with_observer(|| subscriber.update_if_necessary())
{
first_run = false;
subscriber.clear_sources(&subscriber);
{
subscriber.clear_sources(&subscriber);
let old_value =
mem::take(&mut *value.write().or_poisoned());
let new_value = owner.with_cleanup(|| {
subscriber.with_observer(|| fun(old_value))
});
*value.write().or_poisoned() = Some(new_value);
let old_value =
mem::take(&mut *value.write().or_poisoned());
let new_value = owner.with_cleanup(|| {
subscriber.with_observer(|| fun(old_value))
});
*value.write().or_poisoned() = Some(new_value);
}
}
}
}
});
RenderEffect { value, inner }
});
RenderEffect { value, inner }
}
erased(Box::new(fun))
}
}