Ducks

Ducks are objects that describe redux state transitions.

The goal is to isolate all code for every action in a single place. Making transitions simpler to maintain, and quicker to analyze.

    const increment = {

      // This is typically what you would use for the name of your action
      // creator.
      name: 'increment',

      // Your action creator
      action: payload => ({ payload }),

      // Your transducer,
      // You've seen them as the case blocks within switch statements.
      reducer: (s, { payload }) =>
        payload
          ? s + payload
          : s + 1
    }
  


Modules

Ducks get organized into modules. Each module needs an initial state, a few selectors for selecting information (functions that recieve the modules state as the argument), and a list of ducks for modifying pieces of that state.

    const increment = { ... };
    const decrement = { ... };

    const module = {
      // The initial state should reflect the state that your ducks will be
      // manipulating.
      init: 0,

      // A map of selectors, for extracting state from the above contract.
      select: {
        val  : s => s,
        succ : s => s + 1,
        pred : s => s - 1,
      },

      // The list of all of the ducks that interact at this level with the
      // state.
      ducks: [
        increment,
        decrement,
      ]
    };
  


Deux

The deux function is very powerful. It has the ability to merge modules and organize our branches of state. This is done by recursively descending the spec we provide. The return value, uses the shape of the tree to annotate and patch the action and reducer functions to account for all of the nesting.

This enables us to reuse state, in multiple places, such as the counter in this example:

    const app = deux({
      example: {
        phonebook,
        counter,
      },
      otherState: {
        todo,
        counter,
      }
    });
  


Rere

Once we have a state declared the way we want, we'll call rere on it to extract everything we'll need to plug in to our redux store.

For convinience sake, there is a function called reredeux which composes both rere and deux. Often times you'll only need this function, since you can technically use it anywhere deux is used.

    const counter = {
      init: 0,
      select: { val: s => s },
      ducks: [ increment, decrement ]
    };
    const { reducer, init, actions, select } =
      reredeux({
        data: {
          counter,
        },
        otherData: {
          counter,
        }
      });

    const store = createStore(reducer, init);
    store.getState();
    // { data: { counter: 0 }, otherData: { counter: 0 } }
  


Actions and Selectors

Once you've created your store, you'll have access to actions and select. Both objects mimic the shape of your state tree.

    store.dispatch(actions.data.counter.increment());
    store.dispatch(actions.data.counter.increment());

    select.data.counter.val(store.getState()) // 2
    select.otherData.counter.val(store.getState()) // 0

    store.dispatch(actions.data.counter.decrement());

    select.data.counter._(store.getState()) // 1
    select.otherData.counter.val(store.getState()) // 0
  

NOTE: there is a convinience selector added to every level of the state tree. This selector is denoted with the _ character, and will return all of the state at that level on down.