tincan 0.3.0

A lightweight reactive state management library for Rust.
Documentation
use crate::runtime::RuntimeInner;
use std::cell::RefCell;
use std::rc::{Rc, Weak};

/// A side effect that runs when its dependencies change.
///
/// Effects automatically track signal reads and re-run when those signals change.
/// The effect runs immediately on creation by default to establish initial dependencies.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// use tincan::Scope;
/// use std::cell::Cell;
/// use std::rc::Rc;
///
/// let cx = Scope::new();
/// let count = cx.signal(0);
/// let counter = Rc::new(Cell::new(0));
///
/// let _effect = cx.effect({
///     let count = count.clone();
///     let counter = Rc::clone(&counter);
///     move || {
///         let _ = count.get();
///         counter.set(counter.get() + 1);
///     }
/// });
///
/// // Effect runs immediately
/// assert_eq!(counter.get(), 1);
/// ```
pub struct Effect {
    id: usize,
    runtime: Weak<RefCell<RuntimeInner>>,
}

impl Effect {
    /// Create a new effect within a scope (internal use).
    /// Runs immediately by default.
    pub(crate) fn new_in_scope<F>(effect: F, id: usize, runtime: Rc<RefCell<RuntimeInner>>) -> Self
    where
        F: Fn() + 'static,
    {
        Self::new_in_scope_with_lifecycle(effect, id, runtime, true)
    }

    /// Create an effect with control over immediate execution (internal use).
    /// Used by Signal::map and Signal::zip which need deferred execution.
    pub(crate) fn new_in_scope_with_lifecycle<F>(
        effect: F,
        id: usize,
        runtime: Rc<RefCell<RuntimeInner>>,
        run_immediately: bool,
    ) -> Self
    where
        F: Fn() + 'static,
    {
        let effect = Rc::new(effect);
        let effect_clone = Rc::clone(&effect);

        // Register the effect with the runtime
        runtime.borrow().create_observer(id, move || {
            effect_clone();
        });

        if run_immediately {
            // Run immediately within the observer context to track dependencies
            RuntimeInner::with_observer_scoped(&runtime, id, || {
                effect();
            });
        }

        Self {
            id,
            runtime: Rc::downgrade(&runtime),
        }
    }
}

impl Drop for Effect {
    fn drop(&mut self) {
        if let Some(runtime) = self.runtime.upgrade() {
            runtime.borrow().remove_observer(self.id);
        }
    }
}