Files
dioxus/packages/core/docs/reactivity.md
T
Evan Almloff d8d11db403 Boxed and mapped signals preparing for stores (#4413)
This PR contains just the breaking changes required for the upcoming store implementation. It adds two new primitives:

Mutable mapped signals (without a different subscriber list). This lets you scope a writable value and pass it to a child component. It requires two closures to map the immutable and mutable references to the inner signal. With stores, these closures will be derived
Boxed readable and writable values (ReadSignal, and WriteSignal). Any readable or writable types can be converted to a read signal or write signal with the boxed method or built in types are automatically converted into the boxed variants when passing into props


* map signals mutably

* boxed signals

* remove mutable borrow type from boxed writable

* improve map mut example

* restore boxed_mut helper

* make boxed readable/writable types copy

* Move away from the ReadOnlySignal alias in examples

* automatically convert Write

* Fix some tests

* ReadSignal and WriteSignal

* move read only signal alias

* make maps copy

* store prototype

* implement derive macro

* Handle generics in the derive macro

* Make Selector boxable

* derive boxed conversion

* Get rid of store type

* Remove hardcoded type from derive macro

* selector -> store

* Refactor store implementation

* more vec methods

* todo mvc store example

* Implement Storable for HashMap

* fix write

* restore sorting logic in todomvc

* derive readable and writable

* improve todomvc store example

* Result store

* Option store

* collect dead subscribers

* fix clippy and formatting

* recognize some common foreign types automatically

* fix foreign stores in props

* slice and array selectors

* clean up store example

* remove stores

* writable is child owned

* fix typo

* fix clippy

* fix doc tests
2025-07-31 13:19:55 -07:00

113 lines
4.0 KiB
Markdown

# Reactivity
The core of dioxus relies on the concept of reactivity. Reactivity is the system that updates your app when state changes.
There are two parts to reactivity: **Reactive Contexts** and **Tracked Values**.
## Reactive Contexts
Reactive Contexts keep track of what state different parts of your app rely on. Reactive Context show up throughout dioxus: Component, use_effect, use_memo and use_resource all have their own reactive contexts:
```rust, no_run
# use dioxus::prelude::*;
let count = use_signal(|| 0);
// The reactive context in the memo knows that the memo depends on the count signal
use_memo(move || count() * 2);
```
## Tracked Values
Tracked values are values that reactive contexts know about. When you read a tracked value, the reactive context will rerun when the value changes. Signals, Memos, and Resources are all tracked values:
```rust, no_run
# use dioxus::prelude::*;
// The count signal is tracked
let count = use_signal(|| 0);
// When you read the count signal, the reactive context subscribes to the count signal
let double_count = use_memo(move || count() * 2);
```
## Reactivity
Reactivity is the system that combines reactive context and tracked values to update your app when state changes.
You can use reactivity to create derived state and update your app when state changes.
You can derive state from other state with [`use_memo`](https://docs.rs/dioxus/latest/dioxus/prelude/fn.use_memo.html).
```rust, no_run
use dioxus::prelude::*;
let mut count = use_signal(|| 0);
let double_count = use_memo(move || count() * 2);
// Now whenever we read double_count, we know it is always twice the value of count
println!("{}", double_count); // Prints "2"
// After we write to count, the reactive context will rerun and double_count will be updated automatically
count += 1;
println!("{}", double_count); // Prints "4"
```
You can also use reactivity to create derive state asynchronously. For example, you can use [`use_resource`](https://docs.rs/dioxus/latest/dioxus/prelude/fn.use_resource.html) to load data from a server:
```rust, no_run
use dioxus::prelude::*;
let count = use_signal(|| 0);
let double_count = use_resource(move || async move {
// Start a request to the server. We are reading the value of count to format it into the url
// Since we are reading count, this resource will "subscribe" to changes to count (when count changes, the resource will rerun)
let response = reqwest::get(format!("https://myserver.com/doubleme?count={count}")).await.unwrap();
response.text().await.unwrap()
});
```
## Non Reactive State
You can use plain Rust types in Dioxus, but you should be aware that they are not reactive. If you read the non-reactive state, reactive scopes will not subscribe to the state.
You can make non-reactive state reactive by using the `Signal` type instead of a plain Rust type or by using the `use_reactive` hook.
```rust, no_run
use dioxus::prelude::*;
// ❌ Don't create non-reactive state
let state = use_hook(|| std::cell::RefCell::new(0));
// Computed values will get out of date if the state they depend on is not reactive
let doubled = use_memo(move || *state.borrow() * 2);
// ✅ Create reactive state
let state = use_signal(|| 0);
// Computed values will automatically keep up to date with the latest reactive state
let doubled = use_memo(move || state() * 2);
// ❌ Don't depend on non-reactive prop state in memos/resources
#[component]
fn MyComponent(state: i32) -> Element {
let doubled = use_memo(move || state * 2);
todo!()
}
// ✅ Wrap your props in ReadSignal to make them reactive
#[component]
fn MyReactiveComponent(state: ReadSignal<i32>) -> Element {
let doubled = use_memo(move || state() * 2);
todo!()
}
```
If your state can't be reactive, you can use the `use_reactive` hook to make it reactive.
```rust, no_run
use dioxus::prelude::*;
let state = rand::random::<i32>();
// You can make the state reactive by wrapping it in use_reactive
let doubled = use_memo(use_reactive!(|state| state * 2));
```