A Signal<T> is a Copy-able wrapper around a value that automatically tracks reads and writes. When you read a signal in a component, that component subscribes to updates. When you write to a signal, all subscribed components re-render.
use dioxus::prelude::*;let mut count = Signal::new(0);// Reading subscribes the current componentlet value = count();// Writing notifies all subscriberscount += 1;
let mut count = use_signal(|| 0);// Peek doesn't subscribe this componentlet value = count.peek();// This won't cause the component to re-render*count.write() += 1;
Use peek() when you need to read a value without triggering re-renders on updates.
let items = use_signal(|| vec![1, 2, 3]);// Iterate directly over the signalfor item in items.iter() { println!("Item: {}", item);}// Use in rsxrsx! { for item in items.iter() { li { "{item}" } }}
let mut signal = use_signal(|| 0);// ❌ This will panic - holding read across writelet read = signal.read();signal += 1; // Panic! Can't write while readingprintln!("{}", read);// ✅ Use blocks to scope borrows{ let read = signal.read(); println!("{}", read);} // read droppedsignal += 1; // OK now// ✅ Or use with()signal.with(|value| println!("{}", value));signal += 1; // OK
Don’t hold signal guards across await points - it can cause panics.
// ❌ Don't do thisuse_future(move || async move { let value = signal.read(); // While awaiting, other code might try to read/write sleep(Duration::from_secs(1)).await; // Danger! println!("{}", value);});// ✅ Clone the value firstuse_future(move || async move { let value = signal(); // Clone the value sleep(Duration::from_secs(1)).await; println!("{}", value);});
let signal = use_signal(|| 0);let enabled = use_signal(|| true);use_effect(move || { if enabled() { // Only subscribes when enabled is true println!("Value: {}", signal()); }});