ATTENTION: Project has been renamed to Yewdux
yew-state will no longer receive updates. Please use Yewdux instead.
State management in Yew can become complex, especially when many components need mutable access to shared state. This crate simplifies that access (comparable to context hooks in React) so you can spend less time writing boilerplate!
Install
Install this package from your terminal:
$ cargo install cargo-edit
$ cargo add yew-state
Or add it to your project's Cargo.toml
:
[]
= "^0.4"
Quickstart
SharedStateComponent
Give your components shared state by adding SharedHandle
properties and wrapping them in
SharedStateComponent
.
use *;
use ;
use NeqAssign;
type Handle = ;
type App = ;
StateView
StateView
components are a convenient way to write quick and simple access to shared state. At the
cost of a little control, they require almost no boilerplate:
use *;
use ;
Usage
Shared state is accessed through state handles (SharedHandle
or StorageHandle
), which are
managed by the component wrapper SharedStateComponent
. This wrapper takes care of all the boring
message passing for sending and receiving updates to state. Updated state is then passed to your
component like any other properties, handled in Component::change
.
Add the following to your component to give it shared state (other details omitted):
use *;
use ;
type MyComponent = ;
State Handles
state
provides a reference to current state:
let state: &MyState = self.handle.state;
reduce
lets you mutate shared state directly:
// SharedHandle<UserState>
self.handle.reduce;
reduce_callback
allows modifying shared state from a callback:
// SharedHandle<usize>
let onclick = self.handle.reduce_callback;
html!
reduce_callback_with
provides the fired event as well:
// SharedHandle<UserState>
let oninput = self
.handle
.reduce_callback_with;
html!
reduce_callback_once
and reduce_callback_once_with
are also provided for Callback::Once
variants.
More on StateView
StateView
supports a couple other hooks in addition to view
which allow a little more control
over component behavior: rendered
and change
.
use *;
use ;
SharedState Properties
State handles derive Properties
for convenience, but they can also be used from your own
properties. Just implement SharedState
:
TODO: Add derive macro for SharedState
Persistence
To make state persistent use a StorageHandle
. This requires your state to also implement
Serialize
, Deserialize
, and Storable
.
use ;
use ;
;
Now your state won't be lost on refresh or if the user navigates away.
TODO: Add derive macro for Storable
Scoping
By default all components use the same scope. Components only share state with other components that have the same scope; changes to shared state in one scope do not affect components in a different one.
To change a component's scope simply give it a different scope type:
;
type MyComponent = ;
Custom state handlers
To customize state management for your app, you can define your own StateHandler
. This behaves
similarly to an agent, and lets your components create their own bridge for message passing (without
a component wrapper and handle).
There are two examples demonstrating how state handlers are used. examples/handler_bridge
shows a
simple bridge that talks directly to your state handler. examples/service_bridge
also talks to
your state handler, as well as its parent StateService. This is useful when you want to receive
updates to state (basically what the component wrapper does for you).
Example
This example demonstrates how two counters with different scopes can be incremented independently.
use *;
use ;
;
;
;
Tips and Tricks
Performance
CoW says moo
We use a clone on write pattern to make changes to shared state. This lets components decide when to
receive changes in Component::change
. If you need to share state that is expensive to clone, be
sure to wrap it in an Rc
!
Break it up
It helps to break up your app state so components only share what they need. This way components aren't
notified of changes that they don't care about. For example layout components might share a
LayoutState
that can be updated without affecting your other components every time the layout
changes.
No spaghetti please
For sanity's sake try to only modify shared state from a few components. As your app grows in complexity it can become increasingly difficult to keep track of which components are mutating state.
Beware infinite render loops
Consider our quickstart example with a slight modification:
This will compile, but as soon as view_counter
is rendered your app will freeze as the counter
infinitely increments itself.
The above example can be fixed like so:
if *handle.state == 0
This is a simple example but it can happen many different ways. If your app is freezing, chances are you've got a component caught in a render loop.