TypeScript icon, indicating that this package has built-in type declarations

0.6.0-0 • Public • Published

Redux Executor

Npm version Build Status Coverage Status

Redux enhancer for handling side effects.

Warning: API is not stable yet, will be from version 1.0

Table of Contents

  1. Installation
  2. Motivation
  3. Concepts
  4. Composition
  5. Narrowing
  6. Execution order
  7. API
  8. Code Splitting
  9. Typings
  10. License


Redux Executor requires Redux 3.1.0 or later.

npm install --save redux-executor

This assumes that you’re using npm package manager with a module bundler like Webpack or Browserify to consume UMD modules.

To enable Redux Executor, use createExecutorEnhancer with createStore:

import { createStore } from 'redux';
import { createExecutorEnhancer } from 'redux-executor';
import rootReducer from './reducers/index';
import rootExecutor from './executors/index';
const store = createStore(

Redux DevTools

To use Redux Executor with Redux DevTools, you have to be careful about enhancers order. It's because Redux Executor do not pass commands to next enhancers so it has to be placed after DevTools (to see commands).

const devToolsCompose = window && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
const enhancer = compose(
    // ...some enhancers
    applyMiddleware(/* ...some middlewares */)


There are many clever solutions to deal with side-effects in redux application like redux-thunk or redux-saga. The goal of this library is to be simpler than redux-saga, easier to test and more pure than redux-thunk.

Typical usage of executor is to fetch some external resource, for example list of posts. It can look like this:

import { handleCommand } from 'redux-executor';
import { postApi } from './api/postApi';
// import action creators
import { postsRequested, postsResolved, postsRejected } from './actions/postActions'; 
// postExecutors.js
function fetchPostsExecutor(command, dispatch, getState) {
  return postApi.list(
  .then((posts) => dispatch(postsResolved(posts)))
  .catch((error) => dispatch(postsRejected(error)));
export default handleCommand('FETCH_POSTS()', fetchPostsExecutor);
// somwhere else in code

So what is the difference between executor and thunk? With executors you have separation between side-effect request and side-effect call. It means that you can omit second phase and not call side-effect (if you not bind executor to the store). With this design it's very easy to write unit tests. If you use Redux DevTools it will be also easier to debug - all commands will be logged in debugger.

I recommend to use redux-executor with redux-detector library. In this combination you can for example detect if client is on given url and dispatch fetch command. All, excluding executors, will be pure.


An Executor

It may sounds a little bit scary but there is nothing to fear - executor is very pure and simple function.

type Executor<S> = (command: Action, dispatch: ExecutableDispatch<S>, getState: GetState<S | undefined>) => Promise<void> | void;

Like you see above, executor takes an action (called command in executors), enhanced dispatch function and state. It can return a Promise to provide custom execution flow.

A Command

Command is an action with specific type format - COMMAND_TYPE() (like function call, instead of COMMAND_TYPE). The idea behind is that it's more clean to split actions to two types: normal actions (we will call it events) that tells what has happened and commands that tells what should happen.

Events are declarative like: { type: 'USER_CLICKED_FOO' }, commands are imperative like: { type: 'FETCH_FOO()' }.

The reaction for event is state reduction (by reducer) which is pure, the reaction for command is executor call which is unpure.

Another thing is that events changes state (by reducer), commands not. Because of that command dispatch doesn't call store listeners (for example it doesn't re-render React application).


You can pass only one executor to the store, but with combineExecutors and reduceExecutors you can mix them to one executor. For example:

import { combineExecutors, reduceExecutors } from 'redux-executor';
import { fooExecutor } from './fooExecutor';
import { barExecutor } from './barExecutor';
import { anotherExecutor } from './anotherExecutor';
// our state has shape:
// {
//   foo: [],
//   bar: 1
// }
// We want to bind `fooExecutor` and `anotherExecutor` to `` branch (they should run in sequence)
// and also `barExecutor` to `` branch.
export default combineExecutors({
  foo: reduceExecutors(
  bar: barExecutor


To re-use executors we can also mount them to some state branch. To do this, use mountExecutor function with state selector and executor.

import { mountExecutor } from 'redux-executor';
// our state has shape:
// {
//   foo: [1, 3],
// }
// We want to bind `fooExecutor` to the length of `` branch
function fooExecutor(command, dispatch, getState) {
  console.log(getState()); // > 2
export default mountExecutor((state) =>, fooExecutor);


By default executor runs for every command. To limit executor to given command type, you can write if statement or use handleCommand/handleCommands function.

For example to limit fooExecutor to command FOO(), barExecutor to command BAR() and mix them into one executor:

import { handleCommand, handleCommands, reduceExecutors } from 'redux-executor';
function fooExecutor(command, dispatch, getState) { 
  // foo executor logic
function barExecutor(command, dispatch, getState) {
  // bar executor logic  
// OPTION 1: handleExecutor + reduceExecutors
export default reduceExecutors(
  handleCommand('FOO()', fooExecutor),
  handleCommand('BAR()', barExecutor)
// OPTION 2: handleCommands
export default handleCommands({
  'FOO()': fooExecutor,
  'BAR()': barExecutor

Execution order

Sometimes we want to dispatch actions in proper order. To do this, we have to return promise from executors we want to include to our execution order. If we dispatch command, dispatch method will return action (it's redux behaviour) with additional promise field that contains promise of our side-effects. Keep in mind that this promise is the result of calling all combined or reduced executors (built-in implementations uses Promise.all, see reduceExecutors, combineExecutors). Because of that you should not rely on promise content - in fact it should be undefined.

Lets say that we want to run firstCommand and then secondCommand and thirdCommand in parallel. The easiest solution is:

// import action creators
import { firstCommand, secondCommand, thirdCommand } from './commands/exampleCommands';
function firstThenNextExecutor(command, dispatch, getState) {
  return dispatch(firstCommand()).promise
  .then(() => Promise.all([
export default handleCommand('FIRST_THEN_NEXT()', firstThenNextExecutor);

To be more declarative and to reduce boilerplate code, you can create generic sequenceCommandExecutor and parallelCommandExecutor.

// executionFlowExecutors.js
import { handleCommand } from 'redux-executor';
export const sequenceCommandExecutor = handleCommand(
  (command, dispatch) => command.payload.reduce(
    (promise, command) => promise.then(() => dispatch(command).promise || Promise.resolve()),
export const parallelCommandExecutor = handleCommand(
  (command, dispatch) => Promise.all( => dispatch(command).promise || Promise.resolve())
  ).then(() => undefined) // we should return Promise<void> because we should not rely on promise result

With these executors we can create action creator instead of executor for the previous example.

// import action creators
import { firstCommand, secondCommand, thirdCommand } from './commands/exampleCommands';
import { sequenceCommand, parallelCommand } from './commands/executionOrderCommands';
export default function firstThenNext() {
  return sequenceCommand([
// it will return
// {
//   type: 'SEQUENCE()',
//   payload: [
//     { type: 'FIRST()' },
//     { 
//       type: 'PARALLEL()',
//       payload: [
//         { type: 'SECOND()' },
//         { type: 'THIRD()' }
//       ]
//     }
//   ]
// }



type ExecutorsMap<S> = {
  [K in keyof S]: Executor<S[K]>;
function combineExecutors<S>(map: ExecutorsMap<S>): Executor<S>;

Binds executors to state branches and combines them to one executor. Executors will be called in sequence but promise will be resolved in parallel (by Promise.all) Useful for re-usable executors.


type StoreExecutableEnhancer<S> = (next: StoreEnhancerStoreCreator<S>) => StoreEnhancerStoreExecutableCreator<S>;
type StoreEnhancerStoreExecutableCreator<S> = (reducer: Reducer<S>, preloadedState: S) => ExecutableStore<S>;
function createExecutorEnhancer<S>(executor: Executor<S>): StoreExecutableEnhancer<S>;

Creates new redux enhancer that extends redux store api (see ExecutableStore)


interface ExecutableDispatch<S> extends Dispatch<S> {
  <A extends Action>(action: A): A & { promise?: Promise<void> };

It's type of enhanced dispatch method that can add promise field to returned action if you dispatch command.


interface ExecutableStore<S> extends Store<S> {
  dispatch: ExecutableDispatch<S>;
  replaceExecutor(nextExecutor: Executor<S>): void;

It's type of store that has enhanced dispatch method (see ExecutableDispatch) and replaceExecutor method (like replaceReducer).


type Executor<S> = (command: Action, dispatch: ExecutableDispatch<S>, getState: GetState<S | undefined>) => Promise<void> | void;

See Concepts / An Executor


const EXECUTOR_INIT: string = '@@executor/INIT()';

Command of this type is dispatched on init and replaceExecutor call.


type GetState<S> = () => S;

Simple function to get current state (we don't provide state itself because it can change during async side-effects).


function handleCommand<S>(type: string, executor: Executor<S>): Executor<S>;

Limit executor to given command type (inspired by redux-actions).


type ExecutorPerCommandMap<S> = {
  [type: string]: Executor<S>;
function handleCommands<S>(map: ExecutorPerCommandMap<S>): Executor<S>;

Similar to handleCommand but works for multiple commands at once. Map is an object where key is a command type, value is an executor (inspired by redux-actions).


function isCommand(object: any): boolean;

Checks if given object is a command (object.type ends with () string).


function isCommandType(type: string): boolean;

Similar to isCommand but checks only type string.


function mountExecutor<S1, S2>(selector: (state: S1 | undefined) => S2, executor: Executor<S2>): Executor<S1>;

Mounts executor to some state branch. Useful for re-usable executors.


function reduceExecutors<S>(...executors: Executor<S>[]): Executor<S>;

Reduces multiple executors to one. Executors will be called in sequence but promise will be resolved in parallel (by Promise.all). Useful for re-usable executors.

Code Splitting

Redux Executor provides replaceExecutor method on ExecutableStore interface (store created by Redux Executor). It's similar to replaceReducer - it changes executor and dispatches { type: '@@executor/INIT()' }.


If you are using TypeScript, you don't have to install typings - they are provided in npm package.



Package Sidebar


npm i redux-executor

Weekly Downloads






Last publish


  • piotr-oles