A lightweight library for reactive programming with signals in Rust.
Reactivity is a Rust library that provides a flexible reactive programming system. It allows you to create signals that can depend on other signals, with automatic propagation of changes through your dependency graph.
- Two signal implementations:
reactivity::Signalfor single-threaded contextsreactivity::sync::Signalfor thread-safe, multi-threaded contexts
- Clean API for creating and managing signals
- Convenient macro for defining reactive computations
- Support for side effects when signals change
- Fine-grained control over reaction propagation
Add the following to your Cargo.toml:
[dependencies]
reactivity = "0.1.0"use reactivity::Signal;
use reactivity::signal;
fn main() {
// Create a basic signal
let count = signal!(0);
// Create a derived signal that depends on count
let doubled = signal!([count] count * 2);
// Create another signal with a side effect
let message = signal!(<old_val, new_val> [count]
format!("The count is {}", count);
println!("Count changed from {} to {}!", old_val, new_val)
);
// Update the original signal
count.send(5);
// The changes have been automatically propagated
assert_eq!(count.get(), 5);
assert_eq!(doubled.get(), 10);
assert_eq!(message.get(), "The count is 5");
}use std::thread;
use reactivity::sync::Signal;
use reactivity::signal;
fn main() {
// Create a thread-safe signal
let count = signal!(0);
// Create dependent signals
let doubled = signal!([count] count * 2);
// Clone for use in another thread
let count_clone = count.clone();
// Spawn a thread that updates the signal
let handle = thread::spawn(move || {
for i in 1..=5 {
count_clone.send(i);
thread::sleep(std::time::Duration::from_millis(100));
}
});
// Wait for thread to complete
handle.join().unwrap();
// Main thread can access the updated value
assert_eq!(doubled.get(), 10);
}Signal<T>: For single-threaded contexts (usesRcandRefCellinternally)sync::Signal<T>: Thread-safe implementation (usesArcandRwLockinternally)
// Single-threaded signal
use reactivity::Signal;
let x = Signal::new(42);
// Thread-safe signal
use reactivity::sync::Signal;
let y = Signal::new(42);
// Create dependent signals
let a = Signal::new(1);
let b = signal!([a] a * 2);
// Register dependency (b will update when a changes)
a.add_receiver(b);The signal! macro provides a convenient way to create signals. The macro automatically uses the appropriate Signal type based on the context:
// Basic signal with initial value
let x = signal!(5);
// Signal that depends on other signals
let y = signal!([x] x + 10);
// Signal with side effect
let z = signal!(<old_val, new_val> [x, y] {
let sum = x + y;
sum * 2
}; println!("z changed from {} to {}", old_val, new_val));You can specify custom effects that run when signals change:
let x = signal!(1);
let y = signal!(<old_y, new_y> [x] x * 3; {
println!("y changed from {} to {}", old_y, new_y);
// Perform side effects here
});- Use
reactivity::Signalfor single-threaded applications where all signals are accessed from the same thread - Use
reactivity::sync::Signalwhen signals need to be shared across multiple threads
This project is dual-licensed under either of:
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this project by you shall be dual licensed as above, without any additional terms or conditions.