Expand description
Standalone, flexible fine-grained reactivity
This crate brings the benefits of fine-grained reactivity to contexts beyond traditional web UI. Signals are lightweight metadata (4 bytes) - your values stay in your structs.
§Quick Start
ⓘ
use reaktiv::{Signal, Effect, Computed, Transaction, flush_effects};
struct Component {
value: f64,
signal: Signal, // Just 4 bytes of metadata
}
impl Component {
fn set(&mut self, v: f64) {
self.value = v;
self.signal.emit(); // Notify subscribers
}
fn get(&self) -> f64 {
self.signal.track_dependency(); // Auto-track in effects
self.value
}
}
// Effects auto-track dependencies and run immediately
let effect = Effect::new(|| {
let value = component.get(); // Tracks dependency automatically
println!("Value: {}", value);
});
// Batch changes with transactions - effects run once at the end
Transaction::run(|| {
component.set(1.0);
component.set(2.0);
}); // Effect runs once with final value
flush_effects();§Core Types
Signal- Lightweight reactive marker (4 bytes). Callemit()when value changes.Effect- Auto-runs when tracked signals change. Runs immediately on creation.Computed<T>- Cached derived value. Only recomputes when dependencies change.Transaction- Batch multiple changes into one effect run.
§Working with Signals
ⓘ
let signal = Signal::new();
// Inside an effect, track this signal as a dependency
signal.track_dependency();
// Notify all subscribers when value changes
signal.emit();
// Skip UI-only subscribers (useful for preventing update loops)
signal.emit_from_ui();§Creating Effects
ⓘ
// Effect runs immediately and tracks dependencies automatically
let effect = Effect::new(|| {
signal.track_dependency();
println!("Signal changed!");
});
// Skippable effects can be deferred under load
let skippable = Effect::new_skippable(|| {
// Non-critical UI updates
});§Computed Values
ⓘ
// Runs immediately and caches the result
let doubled = Computed::new(|| {
input_signal.track_dependency();
input_value * 2
});
// Defers computation until first access
let lazy = Computed::lazy(|| expensive_computation());
let value = doubled.get(); // Returns cached value§Batching Updates
ⓘ
// Effects run once at the end instead of 3 times
Transaction::run(|| {
signal_a.emit();
signal_b.emit();
signal_c.emit();
}); // All pending effects run here
// Suppress UI-triggered effects during internal updates
Transaction::no_ui_updates(|| {
internal_recalculation();
});§Processing Effects
ⓘ
flush_effects(); // Process all pending effects synchronously
is_processing_scheduled(); // Check if effects are waiting to run
untracked(|| { ... }); // Read signals without tracking dependencies
// For async/event loop integration
spawn_effect_loop(); // Start async effect processingStructs§
- Computed
- Memoized derived value that automatically updates when dependencies change
- Effect
- Side-effectful computation that automatically re-runs when dependencies change
- Effect
Loop - Builder for configuring and spawning the effect processing loop.
- Signal
- Lightweight reactive marker that tracks dependencies without owning data
- Transaction
- Batch multiple signal changes into a single effect run
Constants§
- DEFAULT_
BUDGET - Default time budget for effect processing (~60fps).
- DEFAULT_
DEBOUNCE - Default debounce delay for effect processing.
- DEFAULT_
MAX_ DEBOUNCE - Maximum wait time for debouncing.
Functions§
- flush_
effects - Process all pending effects immediately
- is_
processing_ scheduled - Check if effect processing is scheduled
- spawn_
effect_ loop - Spawn the effect processing loop with default settings.
- untracked
- Run a closure without tracking dependencies