Yew State

Ergonomic access to shared state with optionally persistent session/local storage.

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.

Usage

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(move |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> }

To include the fired event use reduce_callback_with ```rust let oninput = self .handle .reducecallbackwith(|state, i: InputData| state.user.name = i.value);

html! { } ```

Properties with Shared State

Implement SharedState to get shared state in any properties. ```rust

[derive(Clone, Properties)]

pub struct Props { #[propordefault] pub handle: GlobalHandle, }

impl SharedState for Props { type Handle = GlobalHandle;

fn handle(&mut self) -> &mut Self::Handle {
    &mut self.handle
}

} ```

State Persistence

To make state persistent use StorageHandle instead of GlobalHandle. This requires that T also implement Serialize, Deserialize, and Storable. ```rust use serde::{Serialize, Deserialize}; use yew_state::Storable; use yew::services::storage::Area;

[derive(Serialize, Deserialize)]

struct T;

impl Storable for T { fn key() -> &'static str { "myapp.storage.t" }

fn area() -> Area {
    // or Area::Session
    Area::Local
}

} ```

Example

Lets make a counting app using shared state!

First the display: ```rust // display.rs use yew::prelude::*; use yewtil::NeqAssign; use yew_state::{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, 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>

                <Input />
            </div>
        </>
    }
}

} ```