redux-composable-reducers

Transducers (reducer composition) tailored to redux-style actions and state trees.

Usage no npm install needed!

<script type="module">
  import reduxComposableReducers from 'https://cdn.skypack.dev/redux-composable-reducers';
</script>

README

redux-composable-reducers

A collection of functions for composing and simplifying reducers in redux.

🚧 🚧 Under active development. Pull requests and discussion are very welcome. Some functions names and signatures are likely to change. 🚧 🚧

Functions

into-like transducer(keyReducer<reducer>)(withKeyStateReducer<reducer>) transducer(keyReducer<string>)(withKeyStateReducer<reducer>) transducer(keyReducerObject<object>)

branch-like transducer(branchObject<object>)(pathReducer<reducer>) transducer(reducerReducer<reducer>)

reduce-like transducer(<reducer>, ...)(reducer)

Action Transducers

action - Reduce the action.

  • payload - Reduce the action payload.
  • error - Reduce the action error.
  • meta - Reduce the action meta.
  • type - Reduce the action type.

Object Key Transducers

reduceKey - state => state[reduceKey(state, action)] Reduce a key to return as the state. intoObject - Reduce the state into a key. intoObjectKey - Reduce the key into a key. intoArray - Reduce the state into a key.

Utilities

pass - state => state Just pass the state through. asReducer - Coerce value to a reducer.

map - Map all the values of an object or array. reduce - Reduce the reducers with the initial state, and the same action. expand - Create more actions to be reduced. merge - Merge a state and a nextState.

filter - Apply next reducer if a filter reduces to truthy. branch - Reduce a reducer and then reduce with that reducer. compose - Combine transducers. initialState - Initialize the state before reducing. dependency - Reduce when something on the state has changed. toAction - Set the action of a reducer to a reduced value. toState - Set the state of a reducer to a reduced value.

Why?

  • The ability leverage and re-use code, even for complex behavior.

  • Simpler measures of correct-ness than the implied methodology.

Drawbacks

  • Composed functions are often less performant than well-written, inlined versions.

  • Composed functions can be hard to debug.

Gotchas

Concepts

Reducers

Reducers are functions that take two or fewer arguments and return a value.

(accumulator, value) => accumulator + value;

Reducers can be deterministic, stable, normal, consistent, and declarative.

  • Deterministic functions return the same value when given the same arguments. f(a, b) == f(a, b)
  • Stable functions return the same value when setting their first argument to their output. f(a, b) == f(f(a, b), b)
  • Normal functions return the same value, but for any second argument, in any order. f(f(a, c), b) == f(f(f(a, b), c), b)
    • In practise, this implies eventual consistency of the data and resilience to duplicate messages.
  • Declarative functions never mutate the given arguments.
  • Consistent functions always return a value of the same type and never return undefined.

Transducers

Transducers are functions that take one or more reducers and return a reducer.

Transducer Reducers

Transducer-reducers are functions that one or more reducers or transducers and return a transducer.

Examples

Obligatory Todos Example

intoObjectKey({
  todos: compose(
    initialState([]),
    type({
      ADD_TODO: intoArray(todos => todos.length)(
        intoObjectKey({
          id: action("id"),
          text: action("text"),
          completed: false
        })
      ),
      TOGGLE_TODO: intoArray((todos, action) =>
        todos.findIndex(todo => todo.id === action.id)
      )(
        intoObjectKey({
          completed: completed => !completed
        })
      )
    })
  ),
  visibility: compose(
    initialState(VisibiltyFilters.SHOW_ALL),
    type({
      SET_VISIBILITY_FILTER: action("filter")
    })
  )
});

Todo

  • More complete examples.
  • Thorough tests for every function.
  • Document through JSDoc and Typescript.
  • Convert to Typescript.
  • Optimize functions.
  • Non-reducer options through wrappers.
  • Streamline and simplify functions and settle on names for functions.