This crate provides ergonomic access to shared state and optional persistent storage (session or local).
Initially this was a PR, but became big enough to warrant a standalone crate.
This is just one approach to shared state! It uses a component wrapper to manage message passing, and Rc
to minimize cloning. If you have suggestions please open an issue, or join in on the
discussion.
Give your component GlobalHandle
properties and wrap it with SharedStateComponent
.
This may be done for any T
that implements Clone
+ Default
.
```rust
struct Model {
handle: GlobalHandle
impl Component for Model {
type Properties = GlobalHandle
type MyComponent = SharedStateComponent
Access current state with state
.
rust
let state: &T = self.handle.state();
Modify shared state from anywhere using reduce
rust
// GlobalHandle<MyAppState>
self.handle.reduce(|state| state.user = new_user);
or from a callback with reduce_callback
.
rust
// GlobalHandle<usize>
let onclick = self.handle.reduce_callback(|state| *state += 1);
html! {
<button onclick = onclick>{"+1"}</button>
}
reduce_callback_with
provides the fired event.
```rust
let oninput = self
.handle
.reducecallbackwith(|i: InputData, state| state.user.name = i.value);
html! { } ```
Get shared state in custom props with SharedState
.
```rust
pub struct Props {
#[propordefault]
pub handle: GlobalHandle
impl SharedState for Props {
type Handle = GlobalHandle
fn handle(&mut self) -> &mut Self::Handle {
&mut self.handle
}
} ```
Persistent storage requires that T
also implement Serialize
,
Deserialize
, and Storable
.
```rust
use serde::{Serialize, Deserialize};
use yew_state::Storable;
use yew::services::storage::Area;
struct T;
impl Storable for T { fn key() -> &'static str { "myapp.storage.t" }
fn area() -> Area {
// or Area::Session
Area::Local
}
} ```
Then use StorageHandle
instead of GlobalHandle
.
Lets make a counting app using shared state!
First the display: ```rust // display.rs use yew::prelude::*; use yewtil::NeqAssign; use yew_state::{Handle, GlobalHandle, SharedStateComponent};
pub struct Model {
handle: GlobalHandle
impl Component for Model {
type Message = ();
type Properties = GlobalHandle
fn create(handle: Self::Properties, _link: ComponentLink<Self>) -> Self {
Self { handle }
}
fn update(&mut self, _msg: Self::Message) -> ShouldRender {
true
}
fn change(&mut self, handle: Self::Properties) -> ShouldRender {
self.handle.neq_assign(handle)
}
fn view(&self) -> Html {
html! {
<p>{ format!("Count: {}", self.handle.state()) }</p>
}
}
}
pub type Display = SharedStateComponent
Now for the button: ```rust // input.rs use yew::prelude::*; use yewtil::NeqAssign; use yew_state::{GlobalHandle, Handle, SharedStateComponent};
pub struct Model {
handle: GlobalHandle
impl Component for Model {
type Message = ();
type Properties = GlobalHandle
fn create(handle: Self::Properties, _link: ComponentLink<Self>) -> Self {
Model { handle }
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
true
}
fn change(&mut self, handle: Self::Properties) -> ShouldRender {
self.handle.neq_assign(handle)
}
fn view(&self) -> Html {
let onclick = self.handle.reduce_callback(|state| *state += 1);
html! {
<button onclick = onclick>{"+1"}</button>
}
}
}
pub type Input = SharedStateComponent
Finally the app: ```rust // app.rs use yew::prelude::*;
use crate::{display::Display, input::Input};
pub struct App; impl Component for App { type Message = (); type Properties = ();
fn create(_props: Self::Properties, _link: ComponentLink<Self>) -> Self {
Self
}
fn update(&mut self, _msg: Self::Message) -> ShouldRender {
true
}
fn change(&mut self, _props: Self::Properties) -> ShouldRender {
true
}
fn view(&self) -> Html {
html! {
<>
<Display />
<div>
<p>{"Start counting!"}</p>
<Input />
</div>
</>
}
}
} ```