tiny-duck

1.1.1 • Public • Published

TinyDuck

Tiny Duck

Composable redux reducers

Build Status npm version

Tiny Duck is a small library that allows you to define small, reusable collections of reducer actions and compose them together.

Tiny Duck gives you high code reuse and testabilty for your redux reducers.

Tiny Duck was inspired from the pattern of erikras/ducks-modular-redux where you bundle all of your actions, action types and reducers in the one module.

Getting Started

Tiny Duck is available on npm: npm install tiny-duck

In the follow examples we are going to use Tiny Duck to create an counter workflow and reuse it in multiple sub states.

Let's first start by importing TinyDuck and creating our counter workflow.

import TinyDuck from 'tiny-duck';

// Create counter workflow
const counter = {
  initialState: {
    counter: 0
  },
  UP: (state, action) => ({
    ...state,
    counter: (state.counter + 1)
  }),
  DOWN: (state, action) => ({
    ...state,
    counter: (state.counter - 1)
  })
};

The counter workflow is a plain javascript object with 3 parts.

  • initialState is a reserved word in TinyDuck allowing you to set the initial state.
  • UP and DOWN are both pure functions returning a new state, this is how you can defined your reducer actions.

Defining your actions and workflows separately, you are able to test the functions independently from your reducers.

Now that we have our workflow, we can use Tiny Duck to create our reducer and action types.

const {reducer, actions} = TinyDuck(counter);

// actions => {UP: 'UP', DOWN: 'DOWN'}

TinyDuck returns reducer and actions

  • reducer is the function you give to redux.
  • actions is an object containing all the action types defined, including all nested action types.

TinyDuck is a many arity function allowing you to pass many workflows and merge them together.

const {reducer, actions} = TinyDuck(counter, {
    RESET: (state, action) => ({counter:0})
});

// actions => {UP: 'UP', DOWN: 'DOWN', RESET: 'RESET'}

Namespaces

Optionally TinyDuck can take a string as the first argument. This string will then be used to namespace all relative actions. The default namespace is empty ''.

Namespaces with Tiny Duck work similar to URL's where you can have relative and absolute URL's you can also have relative and absolute namespaces. Absolute namespaces are created by prefixing your namespace with /.

Example:

const {reducer, actions} = TinyDuck('ns', counter);

// actions => {UP: 'ns/UP', DOWN: 'ns/DOWN'}

Tiny Duck Composition

Tiny Duck allows you to compose other TinyDuck as reducers to operate on sub portions of state.

const {reducer, actions, initialState} = TinyDuck({
   counter1: TinyDuck(counter),
   counter2: TinyDuck(counter)
});

/*
actions => {
    counter1: {UP: 'counter1/UP', DOWN: 'counter1/DOWN'}, 
    counter2: {UP: 'counter2/UP', DOWN: 'counter2/DOWN'}
}

initialState => {
    counter1: {counter:0},
    counter2: {counter:0}
}
*/

In the above example you will notice that we can also access the initialState, this allows Tiny Duck to compose and merge the actions and initialState.

In actions we can see that TinyDuck has automatically add the namespace of the sub state we declared. Eg: {UP: 'counter1/UP', DOWN: 'counter1/DOWN'}. This is how Tiny Duck can ensure isolation of action types.

When the action {type: actions.counter1.UP} is passed to the reducer, the action handler only operates on the sub state.

const newState = reducer(initialState, {type: actions.counter1.UP});

/*
newState => {
    counter1: {counter:1},
    counter2: {counter:0}
}
*/

Absolute namespaces can be used to have an action be dispatched to many different action handlers operating on different parts of your redux state.

This is useful if you want one action to impact multiple parts of your state. e.g. server events (via web sockets etc) or LOGOUT event needing many parts of state to be reset.

const {reducer, actions, initialState} = TinyDuck({
   counter1: TinyDuck('/', counter),
   counter2: TinyDuck('/', counter)
});

/*
actions => {
    counter1: {UP: '/UP', DOWN: '/DOWN'}, 
    counter2: {UP: '/UP', DOWN: '/DOWN'}
}

initialState => {
    counter1: {counter:0},
    counter2: {counter:0}
}
*/

With the absolute namespace in place both counter1 and counter2 have the same namespaced action types. This means that when {type: actions.counter1.UP} is passed to the reducer, both handers for /UP will be triggered.

const newState = reducer(initialState, {type: actions.counter1.UP});

/*
newState => {
    counter1: {counter:1},
    counter2: {counter:1}
}
*/

NOTE: The output of TinyDuck can also be passed as an argument directly to TinyDuck. This allows you to have actions with mixed namespace operating on the same subset of state.

WARNING: Absolute namespaces are a double edge sword. You need to be very careful when using them.

License

MIT

Package Sidebar

Install

npm i tiny-duck

Weekly Downloads

0

Version

1.1.1

License

MIT

Last publish

Collaborators

  • karlmikko