dioxus/packages/signals/src/effect.rs

114 lines
3.4 KiB
Rust
Raw Normal View History

2023-08-07 19:32:46 +00:00
use core::{self, fmt::Debug};
use std::fmt::{self, Formatter};
2023-08-08 20:18:15 +00:00
//
2023-08-05 00:23:57 +00:00
use dioxus_core::prelude::*;
2023-08-08 20:18:15 +00:00
use crate::use_signal;
use crate::{dependency::Dependency, CopyValue};
2023-08-05 00:23:57 +00:00
2023-10-17 17:56:12 +00:00
#[derive(Copy, Clone, PartialEq)]
2023-08-05 00:23:57 +00:00
pub(crate) struct EffectStack {
2023-10-17 17:56:12 +00:00
pub(crate) effects: CopyValue<Vec<Effect>>,
}
impl Default for EffectStack {
fn default() -> Self {
Self {
effects: CopyValue::new_in_scope(Vec::new(), ScopeId::ROOT),
}
}
}
impl EffectStack {
pub(crate) fn current(&self) -> Option<Effect> {
self.effects.read().last().copied()
}
2023-08-05 00:23:57 +00:00
}
pub(crate) fn get_effect_stack() -> EffectStack {
match consume_context() {
Some(rt) => rt,
None => {
let store = EffectStack::default();
2023-10-17 18:02:51 +00:00
provide_root_context(store);
store
2023-08-05 00:23:57 +00:00
}
}
}
2023-08-07 23:56:49 +00:00
/// 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(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());
}
}
2023-08-07 23:56:49 +00:00
/// Effects allow you to run code when a signal changes. Effects are run immediately and whenever any signal it reads changes.
2023-08-05 00:23:57 +00:00
#[derive(Copy, Clone, PartialEq)]
pub struct Effect {
2023-08-21 19:42:56 +00:00
pub(crate) source: ScopeId,
2023-08-07 19:32:46 +00:00
pub(crate) callback: CopyValue<Box<dyn FnMut()>>,
2023-10-17 17:56:12 +00:00
pub(crate) effect_stack: EffectStack,
2023-08-07 19:32:46 +00:00
}
impl Debug for Effect {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_fmt(format_args!("{:?}", self.callback.value))
}
2023-08-05 00:23:57 +00:00
}
impl Effect {
pub(crate) fn current() -> Option<Self> {
2023-10-17 17:56:12 +00:00
get_effect_stack().effects.read().last().copied()
2023-08-05 00:23:57 +00:00
}
2023-08-07 23:56:49 +00:00
/// 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.
2023-08-05 00:23:57 +00:00
pub fn new(callback: impl FnMut() + 'static) -> Self {
let myself = Self {
2023-08-21 19:42:56 +00:00
source: current_scope_id().expect("in a virtual dom"),
2023-08-05 00:23:57 +00:00
callback: CopyValue::new(Box::new(callback)),
2023-10-17 17:56:12 +00:00
effect_stack: get_effect_stack(),
2023-08-05 00:23:57 +00:00
};
myself.try_run();
myself
}
/// Run the effect callback immediately. Returns `true` if the effect was run. Returns `false` is the effect is dead.
pub fn try_run(&self) {
if let Ok(mut callback) = self.callback.try_write() {
2023-08-05 00:23:57 +00:00
{
2023-10-17 17:56:12 +00:00
self.effect_stack.effects.write().push(*self);
2023-08-05 00:23:57 +00:00
}
callback();
{
2023-10-17 17:56:12 +00:00
self.effect_stack.effects.write().pop();
2023-08-05 00:23:57 +00:00
}
}
}
}