Effect

Struct Effect 

Source
pub struct Effect { /* private fields */ }
Expand description

A reactive effect that runs a closure whenever its dependencies change.

Effect behaves similarly to an “event listener” or a callback, but it is automatically tied to any signals or memos it reads during execution. When those dependencies change, the effect will re-run.

Note: The closure runs immediately upon creation via Effect::new, so the effect is always initialized with an up-to-date value.

In short:

  • Like a callback: wraps a closure and runs it.
  • Adds tracking: automatically re-runs when dependent signals change.
  • Runs once immediately at creation.

§Examples

§Basic usage

use std::{cell::Cell, rc::Rc};
use reactive_cache::Effect;

let counter = Rc::new(Cell::new(0));
let c_clone = counter.clone();

let effect = Effect::new(move || {
    // This closure runs immediately
    c_clone.set(c_clone.get() + 1);
});

assert_eq!(counter.get(), 1);

§Using inside a struct

use std::{rc::Rc, cell::Cell};
use reactive_cache::{Signal, Memo, Effect};

struct ViewModel {
    counter: Rc<Signal<i32>>,
    double: Rc<Memo<i32>>,
    effect: Rc<Effect>,
    run_count: Rc<Cell<u32>>,
}

let counter = Signal::new(1);
let double = Memo::new({
    let counter = counter.clone();
    move || *counter.get() * 2
});

let run_count = Rc::new(Cell::new(0));
let run_count_clone = run_count.clone();

let effect = Effect::new({
    let double = double.clone();
    move || {
        run_count_clone.set(run_count_clone.get() + 1);
        let _ = double.get();
    }
});

let vm = ViewModel {
    counter: counter.clone(),
    double: double.clone(),
    effect: effect,
    run_count: run_count.clone(),
};

assert_eq!(run_count.get(), 1);
vm.counter.set(4);
assert_eq!(run_count.get(), 2);

Implementations§

Source§

impl Effect

Source

pub fn new(f: impl Fn() + 'static) -> Rc<Effect>

Creates a new Effect, wrapping the provided closure and running it immediately for dependency tracking.

Returns an Rc<Effect> so the effect can be stored and shared as a non-generic type.

§Examples
§Basic usage
use std::{cell::Cell, rc::Rc};
use reactive_cache::Effect;

let counter = Rc::new(Cell::new(0));
let c_clone = counter.clone();

let effect = Effect::new(move || {
    // This closure runs immediately
    c_clone.set(c_clone.get() + 1);
});

assert_eq!(counter.get(), 1);
§Using inside a struct
use std::rc::Rc;
use reactive_cache::{Signal, Memo, Effect};

struct ViewModel {
    counter: Rc<Signal<i32>>,
    double: Rc<Memo<i32>>,
    effect: Rc<Effect>,
}

let counter = Signal::new(1);
let double = Memo::new({
    let counter = counter.clone();
    move || *counter.get() * 2
});

let vm = ViewModel {
    counter: counter.clone(),
    double: double.clone(),
    effect: Effect::new({
        let double = double.clone();
        move || println!("Double is {}", double.get())
    }),
};

counter.set(3);
assert_eq!(double.get(), 6);
Source

pub fn new_with_deps(f: impl Fn() + 'static, deps: impl Fn()) -> Rc<Effect>

Creates a new Effect with an additional dependency initializer.

This works like Effect::new, but requires a deps closure to be provided, which will be executed during the initial dependency collection phase.

Important: Dependency tracking is performed only when running deps, not f. The closure f will still be executed when dependencies change, but its execution does not collect new dependencies.

This is useful when your effect closure contains conditional logic (e.g. if/match), and you want to ensure that all possible branches have their dependencies tracked on the first run.

Returns an Rc<Effect> so the effect can be stored and shared as a non-generic type.

§Examples
use std::{cell::Cell, rc::Rc};
use reactive_cache::Effect;
use reactive_macros::{ref_signal, signal};

signal!(static mut FLAG: bool = true;);
signal!(static mut COUNTER: i32 = 10;);

let result = Rc::new(Cell::new(0));
let r_clone = result.clone();

// Effect closure has a conditional branch
let effect = Effect::new_with_deps(
    move || {
        match *FLAG_get() {
            true => {}
            false => {
                r_clone.set(*COUNTER_get());
            }
        }
    },
    // Explicitly declare both `FLAG` and `COUNTER` as dependencies
    move || {
        FLAG();
        COUNTER();
    },
);

assert_eq!(result.get(), 0); // runs with FLAG = true

// Changing `FLAG` to false will trigger the effect
FLAG_set(false);
assert_eq!(result.get(), 10);

// Changing `COUNTER` still triggers the effect, even though
// `FLAG` was true on the first run.
COUNTER_set(20);
assert_eq!(result.get(), 20);

Auto Trait Implementations§

§

impl Freeze for Effect

§

impl !RefUnwindSafe for Effect

§

impl !Send for Effect

§

impl !Sync for Effect

§

impl Unpin for Effect

§

impl !UnwindSafe for Effect

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.