redux-promise-listener

A Redux middleware that allows actions to be converted into Promises

Usage no npm install needed!

<script type="module">
  import reduxPromiseListener from 'https://cdn.skypack.dev/redux-promise-listener';
</script>

README

Redux Promise Listener

NPM Version NPM Downloads Build Status codecov.io styled with prettier

Redux Promise Listener generates an async function that will dispatch a start action, and will resolve or reject the promise when a resolve or reject action is dispatched.

Libraries like redux-promise or redux-promise-middleware are useful for converting promises to actions. Redux Promise Listener does the inverse: converting actions to promises.

Why?

Most of the popular form libraries accept an onSubmit function that is expected to return a Promise that resolves when the submission is complete, or rejects when the submission fails. This mechanism is fundamentally incompatible with action management libraries like redux-saga, which perform side-effects (e.g. ajax requests) in a way that does not let the submission function easily return a promise. Redux Promise Listener is a potential solution.

Usage

Step 1

Create and add the middleware as you would with any Redux middleware. Remember to export the middleware!

// store.js
import { createStore, applyMiddleware } from 'redux'
import createReduxPromiseListener from 'redux-promise-listener'

const reduxPromiseListener = createReduxPromiseListener()
const store = createStore(
  reducer,
  initialState,
  applyMiddleware(...otherMiddleware, reduxPromiseListener.middleware)
)
export const promiseListener = reduxPromiseListener // <---- ⚠️ IMPORTANT ⚠️

export default store

Step 2

If you are using react-redux, your Step 2 is over here.

...

Okay, now that those React nerds are gone...

Wherever you need an async function that dispatches one action and listens for others...

// someFile.js
import { promiseListener } from './store.js'

const generatedAsyncFunction = promiseListener.createAsyncFunction({
  start: 'START_ACTION_TYPE', // the type of action to dispatch when this function is called
  resolve: 'RESOLVE_ACTION_TYPE', // the type of action that will resolve the promise
  reject: 'REJECT_ACTION_TYPE' // the type of action that will reject the promise
})

// This structure is in the shape:
// {
//   asyncFunction, <--- the async function that dispatches the start action and returns a Promise
//   unsubscribe    <--- a function to unsubscribe from the Redux store
// }

// dispatches an action { type: 'START_ACTION_TYPE', payload: values }
generatedAsyncFunction.asyncFunction(values).then(
  // called with action.payload when an action of
  // type 'RESOLVE_ACTION_TYPE' is dispatched
  resolvePayload => {
    // do happy stuff 😄
  },

  // called with action.payload when an action of
  // type 'REJECT_ACTION_TYPE' is dispatched
  rejectPayload => {
    // do sad stuff 😢
  }
)

// when done, to prevent memory leaks
generatedAsyncFunction.unsubscribe()

API

createListener: () => PromiseListener

The default export of this library. Creates a Redux middleware, but that also has a function on it called generateAsyncFunction

middleware.generateAsyncFunction: (config: Config) => AsyncFunction

Types

ActionMatcher: Action => boolean

A predicate with which to make decisions about Redux actions.

PromiseListener

An object with the following values:

middleware: Middleware

Redux middleware that should be used when creating your Redux store.

createAsyncFunction: (config: Config) => AsyncFunction

Takes a Config and returns an object containing the async function capable of dispatching an action and resolving/rejecting a Promise upon the dispatch of specified actions, and a function to unsubscribe this listener from the Redux store.

Config

An object with the following values:

start: string

The type of action to dispatch when the function is called.

resolve: string | ActionMatcher

The type of action that will cause the promise to be resolved, or a predicate function that will return true when given the type of action to resolve for.

reject: string | ActionMatcher

The type of action that will cause the promise to be rejected, or a predicate function that will return true when given the type of action to reject for.

setPayload?: (action: Object, payload: any) => Object

A function to set the payload (the parameter passed to the async function). Defaults to (action, payload) => ({ ...action, payload }).

getPayload?: (action: Object) => any

A function to get the payload out of the resolve action to pass to resolve the promise with. Defaults to (action) => action.payload.

getError?: (action: Object) => any

A function to get the error out of the reject action to pass to reject the promise with. Defaults to (action) => action.payload.

AsyncFunction

An object with the following values:

asyncFunction: (payload: any) => Promise<any>

The async function that will dispatch the start action and return a promise that will resolve when the resolve action is dispatched or reject when the reject action is dispatched.

unsubscribe: () => void

A cleanup function that should be called when the async function is no longer needed.

⚠️ Failure to call unsubscribe() may result in a memory leak. ⚠️

If you are using react-redux-promise-listener, this is done for you on componentWillUnmount.