Skip to content

Instantly share code, notes, and snippets.

@FaberVitale
Created June 17, 2021 14:20
Show Gist options
  • Save FaberVitale/476e2977f59c38a703aa63cca56beaf7 to your computer and use it in GitHub Desktop.
Save FaberVitale/476e2977f59c38a703aa63cca56beaf7 to your computer and use it in GitHub Desktop.
A redux action listener middleware
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