Global state with #[store]
The #[store] macro turns a function into a global store. The function is called once, on first access, and every next call returns a clone of the value created back then:
use vertigo::{component, dom, store, Value}; // Created on the first call, then every call returns the same Value #[store] fn cart_count() -> Value<u32> { Value::new(0) } #[component] fn AddToCart() { let add = |_| cart_count().change(|count| *count += 1); dom! { <button on_click={add}>"Add to cart"</button> } } #[component] fn CartBadge() { dom! { <span>"Cart: " {cart_count()}</span> } } dom! { <div> <CartBadge /> <AddToCart /> <AddToCart /> </div> }
Clones of a Value (as well as of Computed, LazyCache and Rc) share the same state, so all components above see and change the same counter. That's why a store should return something that is shared when cloned. Plain data should be wrapped in a Value or an Rc.
Store with parameters
A store can also take parameters. Then a separate value is kept for every set of arguments, which is handy for caching resources:
use vertigo::{dom, store, AutoJsJson, LazyCache, RequestBuilder}; #[derive(AutoJsJson, Clone, PartialEq)] pub struct User { name: String, } // A separate cache for every user id #[store] fn user(id: &u32) -> LazyCache<User> { RequestBuilder::get(format!("/api/users/{id}")) .ttl_minutes(5) .lazy_cache(|status, body| { if status == 200 { Some(body.into::<User>()) } else { None } }) } // Both render the same cache, so the user is fetched only once let header = user(&1).render(|user| dom! { <h3>{&user.name}</h3> }); let footer = user(&1).render(|user| dom! { <p>"Signed in as " {&user.name}</p> }); dom! { <div> {header} {footer} </div> }
Server-side rendering
On the server every request is rendered by a fresh instance of the app, so stores are never shared between users.