This crate provides ergonomic access to shared state via component wrapper, with optional local/session persistence and custom scoping.
Initially this was a PR, but became big enough to warrant a standalone crate.
If you have suggestions please open an issue, or join in on the discussion.
To get started use the SharedStateComponent
wrapper or StateView
component.
Give your component any properties that implement SharedState
then wrap it with
SharedStateComponent
.
IMPORTANT: Changes must be handled in the component's change
method.
```rust
use yew::prelude::*;
use yew_state::{SharedHandle, SharedStateComponent};
use yewtil::NeqAssign;
pub type AppState { pub count: usize, }
pub struct Model {
handle: SharedHandle
impl Component for Model {
type Message = ();
type Properties = SharedHandle
fn create(handle: Self::Properties, _link: ComponentLink<Self>) -> Self {
handle.reduce(|state| state.count = 1); // Magically set count to one for example
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.count += 1);
let count = self.handle.state().count;
html! {
<p>{count}</p>
<button onclick=onclick>{"+1"}</button>
}
}
}
pub type App = SharedStateComponent
For something simpler, StateView
can handle shared state with less boilerplate.
Keep in mind you can't selectively re-render changes this way.
```rust use yew::prelude::*; use yewstate::{viewstate, StateView, SharedHandle};
type CountHandle = SharedHandle
fn viewcounter() -> Html { html! { <> { viewdisplay() } { view_input() } > } }
fn viewdisplay() -> Html { let view = viewstate(|handle: &CountHandle| { html! {
{handle.state()}
} }); html! {fn viewinput() -> Html {
let view = viewstate(|handle: &CountHandle| {
let onclick = handle.reduce_callback(|count| *count += 1);
html! {
}
});
html! {
State handles provide an interface to shared state. SharedHandle
for basic access, while
StorageHandle
also does persistent local/session storage.
IMPORTANT: Changes to state do not take effect immediately! New state must be handled in the
component's change
method.
state
provides current state.
rust
let state: &T = self.handle.state();
reduce
can be used from anywhere to modify shared state.
rust
// SharedHandle<MyAppState>
self.handle.reduce(move |state| state.user = new_user);
reduce_callback
allows modifying shared state from a callback.
rust
// SharedHandle<usize>
let onclick = self.handle.reduce_callback(|state| *state += 1);
html! {
<button onclick=onclick>{"+1"}</button>
}
reduce_callback_with
provides the fired event as well.
```rust
let oninput = self
.handle
.reducecallbackwith(|state, i: InputData| state.user.name = i.value);
html! { } ```
SharedState
can be implemented for any properties.
TODO: This could be a macro. ```rust
pub struct Props {
#[propordefault]
handle: SharedHandle
impl SharedState for Props {
type Handle = SharedHandle
fn handle(&mut self) -> &mut Self::Handle {
&mut self.handle
}
} ```
To make state persistent use StorageHandle
instead of SharedHandle
. This requires state to also implement Serialize
,
Deserialize
, and Storable
.
TODO: This could be a macro. ```rust use serde::{Serialize, Deserialize}; use yew_state::{Storable, Area};
struct T;
impl Storable for T { fn area() -> Area {
Area::Session // Defaults to Area::Local
}
} ```
Sometimes it's useful to only share state within a specific scope. This may be done by providing a
custom scope to SharedStateComponent
or StateView
:
rust
pub struct MyScope;
pub struct MyComponent = SharedStateComponent<MyModel, MyScope>;
This example demonstrates how two counters with different scopes can increment shared state independently. ```rust use yew::prelude::*; use yewstate::{viewstate, StateView, SharedHandle};
struct FooScope; struct BarScope;
type CountHandle = SharedHandle
fn viewinput {handle.state()}
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! {
<>
<h1>{"FooScope"}</h1>
{ view_display::<FooScope>() }
{ view_input::<FooScope>() }
<h1>{"BarScope"}</h1>
{ view_display::<BarScope>() }
{ view_input::<BarScope>() }
</>
}
}
} ```