Awesome
seed_hooks: Local state for seed apps
seed_hooks is a crate that allows you to store state on a per component basis in your seed apps. It is designed as a clone of React Hooks, principally the useState hook.
Here a component is defined as a 'topological aware execution context', this means that the component is aware of its own call-site identity and location in the call tree.
Example:
This is a complete counting button with state implemented in in the Seed framework:
use seed_hooks::*;
#[topo::nested]
fn my_button() -> Node<Msg> {
let count = use_state(|| 0);
div![
count.get(),
button![count.mouse_ev(Ev::Click, |count, _| *count += 1), "Click me"],
]
}
The two most important functions are:
- use_state(|| .. ) stores component state for the type returned by the closure. Returns a state accessor.
#[topo::nested]
function annotation defines the a topologically aware function. Everything executed within the function will have its own unique topological id. The outermost nested function acts as a "root" which resets the topology and enables specific components to have a "topological identity".
How does it work?
-
this relies on the
#![feature(track_caller)]
feature gate to be enabled. -
topo creates a new execution context for every
#[topo::nested]
function or everytopo::call
block. The outermost call re-roots the execution context. The re-rooting allows for consistent execution contexts for the same components as long as you re-root at the start of the base view function. This means that one can store and retrieve local data for an individual component annotated by#[topo::nested]
. -
The execution context is not only determined by the order of calling a
functions but also the source location of these calls. This means that state is consistent and stable even though branching logic might call topologically aware functions in different orders. -
See this awesome talk explaining how topo works: https://www.youtube.com/watch?v=tmM756XZt20
-
a type gets stored with :
let my_string = use_state(||"text")
which stores"text"
in the component for thestr
type. This returns a state accessor struct responsible for getting and setting of the state. -
The accessor is useful because it can be passed to callbacks or cloned or called from different topological contexts. i.e.
my_string.(new_text)
will work no matter where it is called. -
currently seed_hooks exposes a clone to stored values via
get()
and to non-Clone types withget_with()
-
seed_hooks is a Seed specific wrapper around the atomic_hooks crate.