Trait composable::Reducer
source · pub trait Reducer {
type Action;
type Output;
// Required method
fn reduce(&mut self, action: Self::Action, send: impl Effects<Self::Action>);
}Expand description
Reducers are responsible for updating a Store’s state in response to its Actions.
Required Associated Types§
sourcetype Output
type Output
Both unit tests and command line applications often need to return their Store’s final
state. Therefore Store’s into_inner method shuts down the Store and converts its
Reducer into its Output type.
Using a separate Output type, rather than returning the Reducer itself, allows the
Stores to support Reducer types that are not Send.
Reducers that do not need to supportinto_innershould use declaretype Output = Self;as it is a simple, recognizable default.- A
Reducerthat isSendcan also default totype Output = Self;. - Otherwise, the
Reducerwill need to declare anOutputtype that isSendand that can be cratedFromtheReducer’s state.
struct State {
n: Rc<Cell<usize>>, // Rc<Cell<…>> is not Send
};
enum Action { /* … */ }
impl Reducer for State {
type Action = Action;
type Output = usize; // but the usize itself _is_
fn reduce(&mut self, action: Self::Action, send: impl Effects<Self::Action>) { /**/ }
}
impl From<State> for usize {
fn from(value: State) -> Self {
Cell::into_inner(Rc::into_inner(value.n).unwrap_or_default())
}
}In short, you can use type Output = Self; until the compiler says that you can’t.
Required Methods§
sourcefn reduce(&mut self, action: Self::Action, send: impl Effects<Self::Action>)
fn reduce(&mut self, action: Self::Action, send: impl Effects<Self::Action>)
Updates the Reducer’s state in response to the action received.
Additional Actions that need to be performed as a side-effect of an Action should be
invoked on effects.
The logic of the feature is performed by mutating its current State with Actions.
#[derive(Clone, Debug, Default, PartialEq)]
struct State {
n: usize,
}
#[derive(Debug, PartialEq)]
enum Action {
Increment,
Decrement,
}This is most easily done by implementing the Reducer trait directly on it’s State.
use Action::*;
impl Reducer for State {
type Action = Action;
type Output = usize;
fn reduce(&mut self, action: Action, _send: impl Effects<Action>) {
match action {
Increment => {
self.n += 1;
}
Decrement => {
self.n -= 1;
}
}
}
}The reduce method’s first responsibility is to mutate the feature’s current state given an action. Its second responsibility is to trigger effects that feed
their actions back into the system. Currently reduce does not need to run any effects so _effects goes unused.
If the action does need side effects, then more would need to be done. For example, if reduce always maintained an even number for the State, then
each Increment and Decrement would need an effect to follow:1
use Action::*;
impl Reducer for State {
type Action = Action;
type Output = usize;
// This reducer ensures the value is always an even number
fn reduce(&mut self, action: Action, send: impl Effects<Action>) {
match action {
Increment => {
self.n += 1;
if self.n % 2 == 1 {
send.action(Increment); // ⬅︎
}
}
Decrement => {
self.n -= 1;
if self.n % 2 == 1 {
send.action(Decrement); // ⬅︎
}
}
}
}
}- See
TestStorefor a more complete test of this example. - See
Effectsfor all of the effects that can be used within aReducer.
Granted, real code could just adjust the values by two. It is a contrived example to show how to use
effects, after all. ↩