Created
June 17, 2021 14:20
-
-
Save FaberVitale/476e2977f59c38a703aa63cca56beaf7 to your computer and use it in GitHub Desktop.
A redux action listener middleware
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Middleware } from 'redux'; | |
import { isAnyOf, ActionMatchingAnyOf } from '@reduxjs/toolkit'; | |
import { AppMiddlewareApi } from 'store/types'; | |
export interface OnActionListener<T> { | |
(action: T, middlewareApi: AppMiddlewareApi): void; | |
} | |
export interface OnListenerError<T> { | |
(error: unknown, action?: T): void; | |
} | |
/** | |
* A middlewares that triggers a listener when an action matches | |
* one or more predicates. | |
* | |
* Listeners are called **after** reducers receive the matched action and | |
* update the state. | |
* | |
* Errors that occurs inside a listener a passed to an optional | |
* `onListenerError` function that is invoked asynchronously. | |
* @example | |
* ```ts | |
* const updateCount = createAction<number>('update-count'); | |
* | |
* const reduxMiddleware = onAction([updateCount], (action, { getState }) => { | |
* processCountSomehow(action.payload, getState()); | |
* }); | |
* ``` | |
* | |
* @param matchers An array of 1 or more matchers. | |
* @param {OnActionListener<Action>} listener | |
* @param {{OnListenerError<Action>} } onListenerError | |
* @returns Redux middleware. | |
*/ | |
export function onAction<Matchers extends Parameters<typeof isAnyOf>>( | |
matchers: Matchers, | |
listener: OnActionListener<ActionMatchingAnyOf<Matchers>>, | |
onListenerError?: OnListenerError<ActionMatchingAnyOf<Matchers>> | |
): Middleware { | |
if (!Array.isArray(matchers) || matchers.length < 1) { | |
throw new TypeError( | |
`onAction: expected a non empty array of matchers, got ${matchers} instead` | |
); | |
} | |
if (typeof listener !== 'function') { | |
throw new TypeError( | |
`onAction: expected listener to be a function got ${listener} instead` | |
); | |
} | |
let checkAction = isAnyOf(...matchers); | |
const onError = | |
typeof onListenerError === 'function' | |
? onListenerError | |
: (...args: unknown[]) => | |
console.error('action-listener', 'listener-error', ...args); | |
return (middlewareApi) => (next) => (action) => { | |
const output = next(action); | |
try { | |
if (checkAction(action)) { | |
listener(action, middlewareApi); | |
} | |
} catch (middlewareError) { | |
setTimeout(() => { | |
onError(middlewareError, action); | |
}, 0); | |
} | |
return output; | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment