Files
dioxus/packages/stores
Evan AlmloffandJonathan Kelley 58c41f8561 Stores: Externally tracked state and reactive collections (#4483)
* 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

* writable is child owned

* box selector

* read and write to builtins

* re-use the proc macro in the derive macro

* Switch to a extension trait based approach

* switch to extension traits in the derive macro

* transposed method

* derive enums

* Expose Store::new

* remove stores from the prelude

* More consistent naming scheme for SelectorScope methods

* document hashmap methods

* document index extension

* document option ext

* document result ext

* document slice extension

* document the vec extension

* make more methods borrow the store mutably

* Move to impls to have higher priority over the builtin write extension traits

* fix deadlock

* finish stores documentation

* Document the store macro

* cleanup docs and organization

* finish derive docs

* fix typo

* fix clippy

* fix doc tests

* fix derive macro with generics

* fix clippy

* test and fix empty structs

* clean up todomvc store example

* default to a boxed store writer to make it easier to type when providing to the context api

* fix cargo doc

* fix clippy in store example

* relax generational box lifetime bounds

* relax bounds

* relax signal bounds

* fix compilation

* read store

* relax bounds on default store impls

* fix enum derive

* Convert to readsignal and writesignal automatically

* simplify type matching logic

* add write store

* unsized stores

* shrink the path vec type

* use more concrete types

* remove opaque types

* remove copy bounds

* use lens as generic name everywhere

* more consistent borrow semantics

* use a concrete type for the signal metadata and mention references in the map docs

* improve todomvc store docs

* document store derive macro internals

* refactor macro

* improve selector node documentation

* fix clippy

* add btreemap

* fix doc tests

* more option methods

* fix doc

* fix clippy

* add a bunch more result methods

* add a bunch of bounds to the iterators

* fix clippy

* remove option from subscribers

* add store macro to generate extension traits

* fix store example

* fix deadlock

* fix docs

* fix doc tests

* fix clippy

* add stores to public api

* add store macro, and then update todomvc_store example

---------

Co-authored-by: Jonathan Kelley <jkelleyrtp@gmail.com>
2025-08-05 11:30:21 -07:00
..

Dioxus Stores

Stores are an extension to the Dioxus signals system for reactive nested data structures. Each store will lazily create signals for each field/member of the data structure as needed.

By default stores act a lot like [dioxus_signals::Signal]s, but they provide more granular subscriptions without requiring nested signals. You should derive Store on your data structures to generate selectors that let you scope the store to a specific part of your data structure.

use dioxus::prelude::*;
use dioxus_stores::*;

fn main() {
    dioxus::launch(app);
}

// Deriving the store trait provides methods to scope the store to specific parts of your data structure.
// The `Store` macro generates a `count` and `children` method for `Store<CounterTree>`
#[derive(Store, Default)]
struct CounterTree {
    count: i32,
    children: Vec<CounterTree>,
}

fn app() -> Element {
    let value = use_store(Default::default);

    rsx! {
        Tree {
            value
        }
    }
}

#[component]
fn Tree(value: Store<CounterTree>) -> Element {
    // Calling the generated `count` method returns a new store that can only
    // read and write the count field
    let mut count = value.count();
    let mut children = value.children();

    rsx! {
        button {
            // Incrementing the count will only rerun parts of the app that have read the count field
            onclick: move |_| count += 1,
            "Increment"
        }
        button {
            // Stores are aware of data structures like `Vec` and `Hashmap`. When we push an item to the vec
            // it will only rerun the parts of the app that depend on the length of the vec
            onclick: move |_| children.push(Default::default()),
            "Push child"
        }
        ul {
            // Iterating over the children gives us stores scoped to each child.
            for value in children.iter() {
                li {
                    Tree { value }
                }
            }
        }
    }
}