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;
|
2023-08-08 20:06:33 +00:00
|
|
|
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);
|
2023-08-26 19:53:39 +00:00
|
|
|
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));
|
|
|
|
}
|
|
|
|
|
2023-08-08 20:06:33 +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_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) {
|
2023-08-21 19:23:02 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|