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§

source

type Action

All of the possible actions that can be used to modify state.

source

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 support into_inner should use declare type Output = Self; as it is a simple, recognizable default.
  • A Reducer that is Send can also default to type Output = Self;.
  • Otherwise, the Reducer will need to declare an Output type that is Send and that can be crated From the Reducer’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§

source

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 TestStore for a more complete test of this example.
  • See Effects for all of the effects that can be used within a Reducer.

  1. Granted, real code could just adjust the values by two. It is a contrived example to show how to use effects, after all. 

Object Safety§

This trait is not object safe.

Implementations on Foreign Types§

source§

impl<T: Reducer> Reducer for Option<T>

§

type Action = <T as Reducer>::Action

§

type Output = Option<<T as Reducer>::Output>

source§

fn reduce(&mut self, action: Self::Action, send: impl Effects<Self::Action>)

source§

impl<T: Reducer> Reducer for Box<T>

§

type Action = <T as Reducer>::Action

§

type Output = <T as Reducer>::Output

source§

fn reduce(&mut self, action: Self::Action, send: impl Effects<Self::Action>)

Implementors§