add optional dependency variants of selectors and effects
This commit is contained in:
1 parent
e2c08a79ae
commit
d6089bbd35
6 files changed
+174
-1
No files matched your search
@@ -15,3 +15,4 @@ simple_logger = "4.2.0"
|
||||
[dev-dependencies]
|
||||
dioxus = { workspace = true }
|
||||
dioxus-desktop = { workspace = true }
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
@@ -0,0 +1,38 @@
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_signals::*;
|
||||
|
||||
fn main() {
|
||||
dioxus_desktop::launch(app);
|
||||
}
|
||||
|
||||
fn app(cx: Scope) -> Element {
|
||||
let signal = use_signal(cx, || 0);
|
||||
|
||||
use_future!(cx, || async move {
|
||||
loop {
|
||||
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
|
||||
*signal.write() += 1;
|
||||
}
|
||||
});
|
||||
|
||||
let local_state = use_state(cx, || 0);
|
||||
let computed =
|
||||
use_selector_with_dependencies(cx, (local_state.get(),), move |(local_state,)| {
|
||||
local_state * 2 + signal.value()
|
||||
});
|
||||
println!("Running app");
|
||||
|
||||
render! {
|
||||
button {
|
||||
onclick: move |_| {
|
||||
local_state.set(local_state.get() + 1);
|
||||
},
|
||||
"Add one"
|
||||
}
|
||||
div {
|
||||
"{computed}"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/// A dependency is a trait that can be used to determine if a effect or selector should be re-run.
|
||||
pub trait Dependency: Sized + Clone {
|
||||
/// The output of the dependency
|
||||
type Out: Clone + PartialEq;
|
||||
/// Returns the output of the dependency.
|
||||
fn out(&self) -> Self::Out;
|
||||
/// Returns true if the dependency has changed.
|
||||
fn changed(&self, other: &Self::Out) -> bool {
|
||||
self.out() != *other
|
||||
}
|
||||
}
|
||||
|
||||
impl Dependency for () {
|
||||
type Out = ();
|
||||
fn out(&self) -> Self::Out {}
|
||||
}
|
||||
|
||||
/// A dependency is a trait that can be used to determine if a effect or selector should be re-run.
|
||||
pub trait Dep: 'static + PartialEq + Clone {}
|
||||
impl<T> Dep for T where T: 'static + PartialEq + Clone {}
|
||||
|
||||
impl<A: Dep> Dependency for &A {
|
||||
type Out = A;
|
||||
fn out(&self) -> Self::Out {
|
||||
(*self).clone()
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_dep {
|
||||
(
|
||||
$($el:ident=$name:ident $other:ident,)*
|
||||
) => {
|
||||
impl< $($el),* > Dependency for ($(&$el,)*)
|
||||
where
|
||||
$(
|
||||
$el: Dep
|
||||
),*
|
||||
{
|
||||
type Out = ($($el,)*);
|
||||
|
||||
fn out(&self) -> Self::Out {
|
||||
let ($($name,)*) = self;
|
||||
($((*$name).clone(),)*)
|
||||
}
|
||||
|
||||
fn changed(&self, other: &Self::Out) -> bool {
|
||||
let ($($name,)*) = self;
|
||||
let ($($other,)*) = other;
|
||||
$(
|
||||
if *$name != $other {
|
||||
return true;
|
||||
}
|
||||
)*
|
||||
false
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_dep!(A = a1 a2,);
|
||||
impl_dep!(A = a1 a2, B = b1 b2,);
|
||||
impl_dep!(A = a1 a2, B = b1 b2, C = c1 c2,);
|
||||
impl_dep!(A = a1 a2, B = b1 b2, C = c1 c2, D = d1 d2,);
|
||||
impl_dep!(A = a1 a2, B = b1 b2, C = c1 c2, D = d1 d2, E = e1 e2,);
|
||||
impl_dep!(A = a1 a2, B = b1 b2, C = c1 c2, D = d1 d2, E = e1 e2, F = f1 f2,);
|
||||
impl_dep!(A = a1 a2, B = b1 b2, C = c1 c2, D = d1 d2, E = e1 e2, F = f1 f2, G = g1 g2,);
|
||||
impl_dep!(A = a1 a2, B = b1 b2, C = c1 c2, D = d1 d2, E = e1 e2, F = f1 f2, G = g1 g2, H = h1 h2,);
|
||||
@@ -1,9 +1,12 @@
|
||||
use core::{self, fmt::Debug};
|
||||
use std::fmt::{self, Formatter};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use dioxus_core::prelude::*;
|
||||
|
||||
use crate::CopyValue;
|
||||
use crate::dependency::Dep;
|
||||
use crate::{dependency::Dependency, CopyValue};
|
||||
use crate::{use_signal, Signal};
|
||||
|
||||
#[derive(Default, Clone, Copy)]
|
||||
pub(crate) struct EffectStack {
|
||||
@@ -26,6 +29,28 @@ pub fn use_effect(cx: &ScopeState, callback: impl FnMut() + 'static) {
|
||||
cx.use_hook(|| Effect::new(callback));
|
||||
}
|
||||
|
||||
/// Create a new effect. The effect will be run immediately and whenever any signal it reads changes.
|
||||
/// The signal will be owned by the current component and will be dropped when the component is dropped.
|
||||
pub fn use_effect_with_dependencies<D: Dependency>(
|
||||
cx: &ScopeState,
|
||||
dependencies: D,
|
||||
mut callback: impl FnMut(D::Out) + 'static,
|
||||
) where
|
||||
D::Out: 'static,
|
||||
{
|
||||
let dependencies_signal = use_signal(cx, || dependencies.out());
|
||||
cx.use_hook(|| {
|
||||
Effect::new(move || {
|
||||
let deref = &*dependencies_signal.read();
|
||||
callback(deref.clone());
|
||||
});
|
||||
});
|
||||
let changed = { dependencies.changed(&*dependencies_signal.read()) };
|
||||
if changed {
|
||||
dependencies_signal.set(dependencies.out());
|
||||
}
|
||||
}
|
||||
|
||||
/// Effects allow you to run code when a signal changes. Effects are run immediately and whenever any signal it reads changes.
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub struct Effect {
|
||||
|
||||
@@ -10,3 +10,5 @@ mod selector;
|
||||
pub use selector::*;
|
||||
pub(crate) mod signal;
|
||||
pub use signal::*;
|
||||
mod dependency;
|
||||
pub use dependency::*;
|
||||
@@ -1,5 +1,7 @@
|
||||
use dioxus_core::prelude::*;
|
||||
|
||||
use crate::dependency::Dependency;
|
||||
use crate::use_signal;
|
||||
use crate::{get_effect_stack, signal::SignalData, CopyValue, Effect, ReadOnlySignal, Signal};
|
||||
|
||||
/// Creates a new Selector. The selector will be run immediately and whenever any signal it reads changes.
|
||||
@@ -26,6 +28,44 @@ pub fn use_selector<R: PartialEq>(
|
||||
*cx.use_hook(|| selector(f))
|
||||
}
|
||||
|
||||
/// Creates a new Selector with some local dependencies. The selector will be run immediately and whenever any signal it reads or any dependencies it tracks changes
|
||||
///
|
||||
/// Selectors can be used to efficiently compute derived data from signals.
|
||||
///
|
||||
/// ```rust
|
||||
/// use dioxus::prelude::*;
|
||||
/// use dioxus_signals::*;
|
||||
///
|
||||
/// fn App(cx: Scope) -> Element {
|
||||
/// let mut local_state = use_state(cx, || 0);
|
||||
/// let double = use_selector_with_dependencies(cx, (local_state.get(),), move |(local_state,)| local_state * 2);
|
||||
/// local_state.set(1);
|
||||
///
|
||||
/// render! { "{double}" }
|
||||
/// }
|
||||
/// ```
|
||||
pub fn use_selector_with_dependencies<R: PartialEq, D: Dependency>(
|
||||
cx: &ScopeState,
|
||||
dependencies: D,
|
||||
mut f: impl FnMut(D::Out) -> R + 'static,
|
||||
) -> ReadOnlySignal<R>
|
||||
where
|
||||
D::Out: 'static,
|
||||
{
|
||||
let dependencies_signal = use_signal(cx, || dependencies.out());
|
||||
let selector = *cx.use_hook(|| {
|
||||
selector(move || {
|
||||
let deref = &*dependencies_signal.read();
|
||||
f(deref.clone())
|
||||
})
|
||||
});
|
||||
let changed = { dependencies.changed(&*dependencies_signal.read()) };
|
||||
if changed {
|
||||
dependencies_signal.set(dependencies.out());
|
||||
}
|
||||
selector
|
||||
}
|
||||
|
||||
/// Creates a new Selector. The selector will be run immediately and whenever any signal it reads changes.
|
||||
///
|
||||
/// Selectors can be used to efficiently compute derived data from signals.
|
||||
|
||||
Reference in new issue
Block a user