new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

Redux Toolkit · API · all subjects

createlistenermiddleware

30 notes, read out of this brain and free to use. Each one was extracted from a source and is re-checked against its exam.

createListenerMiddleware function signature

createListenerMiddleware is a function that takes optional CreateMiddlewareOptions and returns a ListenerMiddlewareInstance. The options parameter is optional and typed as CreateMiddlewareOptions<ExtraArgument = unknown>. The function signature is: const createListenerMiddleware = (options?: CreateMiddlewareOptions) => ListenerMiddlewareInstance.

CreateMiddlewareOptions interface

CreateMiddlewareOptions interface has two optional properties: extra (type ExtraArgument = unknown) which is an optional extra argument injected into the listenerApi parameter of each listener, equivalent to the extra argument in Redux Thunk middleware; and onError (type ListenerErrorHandler) which is an optional error handler that gets called with synchronous and async errors raised by listener and synchronous errors thrown by predicate. ListenerErrorHandler is a function with signature (error: unknown, errorInfo: ListenerErrorInfo) => void. ListenerErrorInfo interface has one property: raisedBy which is a literal type 'effect' | 'predicate'.

ListenerMiddlewareInstance interface properties

ListenerMiddlewareInstance is a generic interface with type parameters State = unknown, Dispatch extends ThunkDispatch<State, unknown, UnknownAction> = ThunkDispatch<State, unknown, UnknownAction>, and ExtraArgument = unknown. It has four properties: middleware (type ListenerMiddleware<State, Dispatch, ExtraArgument>) which is the actual Redux middleware to add to the store; startListening (type function (options: AddListenerOptions) => Unsubscribe) which adds a new listener entry; stopListening (type function (options: AddListenerOptions & UnsubscribeListenerOptions) => boolean) which removes a listener entry; and clearListeners (type function () => void) which removes all listener entries.

startListening method signature and parameters

startListening accepts options of type AddListenerOptions and returns type UnsubscribeListener. AddListenerOptions interface requires exactly one of four action matching options: type (string for exact action type match), actionCreator (ActionCreator for exact match based on RTK action creator), matcher (Matcher for RTK matcher function), or predicate (ListenerPredicate for custom predicate logic). All four options are optional but one must be provided. The interface also requires effect (type (action: Action, listenerApi: ListenerApi) => void | Promise<void>) which is the callback to run when the action is matched. ListenerPredicate<Action extends ReduxAction, State> is a function with signature (action: Action, currentState?: State, originalState?: State) => boolean. UnsubscribeListener is a function with signature (unsubscribeOptions?: UnsubscribeListenerOptions) => void. UnsubscribeListenerOptions has one optional property: cancelActive (type true) which cancels active instances when unsubscribing.

stopListening method signature and return value

stopListening accepts options of type AddListenerOptions & UnsubscribeListenerOptions and returns a boolean. It removes a given listener entry by comparing function references of the listener and provided actionCreator/matcher/predicate function or type string. By default it does not cancel any active running instances, but passing {cancelActive: true} will cancel running instances. Returns true if the listener entry was removed, or false if no subscription matching the input was found.

clearListeners method signature

clearListeners is a method with signature () => void. It removes all current listener entries and cancels all active running instances of those listeners. It is most useful for test scenarios or app cleanup situations.

addListener action creator

addListener is a standard RTK action creator exported from the package. Dispatching this action tells the middleware to dynamically add a new listener at runtime. It accepts exactly the same options as startListening(). Dispatching this action returns an unsubscribe() callback from dispatch, allowing runtime listener management.

removeListener action creator

removeListener is a standard RTK action creator exported from the package. Dispatching this action tells the middleware to dynamically remove a listener at runtime. It accepts the same arguments as stopListening(). By default it does not cancel any active running instances, but passing {cancelActive: true} cancels running instances. Returns true if the listener entry was removed, or false if no subscription matching the input was found.

clearAllListeners action creator

clearAllListeners is a standard RTK action creator exported from the package. Dispatching this action tells the middleware to remove all current listener entries and cancel all active running instances of those listeners.

take and condition methods only resolve after next action

Both take and condition methods will only resolve after the next action has been dispatched. They do not resolve immediately even if their predicate would return true for the current state at the time they are called.

ListenerEffectAPI interface methods and properties

ListenerEffectAPI<State, Dispatch extends ReduxDispatch<UnknownAction>, ExtraArgument = unknown> extends MiddlewareAPI<Dispatch, State> and includes the following properties and methods: dispatch (inherited from MiddlewareAPI), getState (inherited from MiddlewareAPI), getOriginalState () => State (returns store state before reducers ran, synchronous only), unsubscribe () => void (removes listener, does not cancel active instances), subscribe () => void (re-subscribes previously removed listener or no-op), condition (ConditionFunction type, returns Promise<boolean> resolving when predicate returns true or false on timeout), take (TakePattern type, returns Promise<[Action, State, State] | null> resolving with action tuple or null on timeout), cancelActiveListeners () => void (cancels all other running instances except the caller), cancel () => void (cancels the current listener instance), throwIfCancelled () => void (throws TaskAbortError if listener was cancelled), signal (AbortSignal with aborted property), delay (timeoutMs: number) => Promise<void> (returns cancellation-aware promise), fork<T> (executor: ForkedTaskExecutor<T>) => ForkedTask<T> (launches child task), pause<M> (promise: Promise<M>) => Promise<M> (returns cancellation-aware promise wrapper), and extra (ExtraArgument).

ConditionFunction and TakeFunction signatures

ConditionFunction<Action extends ReduxAction, State> has signature (predicate: ListenerPredicate<Action, State> | (() => boolean), timeout?: number) => Promise<boolean>. It returns true if predicate succeeds or false if timeout (in ms) expires first. TakeFunction<Action extends ReduxAction, State> has signature (predicate: ListenerPredicate<Action, State> | (() => boolean), timeout?: number) => Promise<[Action, State, State] | null>. It resolves to [action, currentState, previousState] tuple or null if timeout expires first.

ForkedTaskAPI interface

ForkedTaskAPI interface has three properties: pause<W>(waitFor: Promise<W>) => Promise<W> which pauses execution waiting for a promise; delay(timeoutMs: number) => Promise<void> which returns a promise resolving after timeout; and signal (AbortSignal) which indicates if task execution is aborted or completed.

TaskResult type and ForkedTask interface

TaskResult<Value> is a union type with three variants: TaskResolved<Value> with readonly status: 'ok' and readonly value: T; TaskRejected with readonly status: 'rejected' and readonly error: unknown; and TaskCancelled with readonly status: 'cancelled' and readonly error: TaskAbortError. ForkedTask<T> interface has two properties: result (Promise<TaskResult<T>>) which resolves with the task result, and cancel() method which cancels the task.

TypeScript pre-typed listener methods pattern

To get proper TypeScript typing for RootState and AppDispatch in startListening and addListener, create pre-typed versions using withTypes method. Create the middleware in a separate file and use withTypes to pass RootState, AppDispatch, and optional ExtraArgument types. Example: export const startAppListening = listenerMiddleware.startListening.withTypes<RootState, AppDispatch, ExtraArgument>(). Similarly for addListener: export const addAppListener = addListener.withTypes<RootState, AppDispatch>(). This follows the same pattern as pre-typed React-Redux hooks.

Basic createListenerMiddleware usage example

import { configureStore, createListenerMiddleware } from '@reduxjs/toolkit' import todosReducer, { todoAdded, todoToggled, todoDeleted } from '../features/todos/todosSlice' const listenerMiddleware = createListenerMiddleware() listenerMiddleware.startListening({ actionCreator: todoAdded, effect: async (action, listenerApi) => { console.log('Todo added: ', action.payload.text) listenerApi.cancelActiveListeners() const data = await fetchData() if (await listenerApi.condition(matchSomeAction)) { listenerApi.dispatch(todoAdded('Buy pet food')) const task = listenerApi.fork(async (forkApi) => { await forkApi.delay(5) return 42 }) const result = await task.result if (result.status === 'ok') { console.log('Child succeeded: ', result.value) } } }, }) const store = configureStore({ reducer: { todos: todosReducer }, middleware: (getDefaultMiddleware) => getDefaultMiddleware().prepend(listenerMiddleware.middleware), })

Listener middleware must be prepended before serializability check

The listener middleware should be added as the first middleware in the chain using prepend() rather than append(), because the middleware can receive actions with functions inside (add and remove listener actions). It must be placed before the serializability check middleware to avoid false positive errors about unserializable actions.

getOriginalState can only be called synchronously

getOriginalState() returns the store state as it existed when the action was originally dispatched, before reducers ran. This function can only be invoked synchronously during the initial dispatch call stack. Calling it asynchronously will throw an error. This restriction exists to avoid memory leaks.

Predicate and effect callbacks timing

All listener predicates and effect callbacks are checked and run after the root reducer has already processed the action and updated the state. The predicate is evaluated against the new state, not the old state. To access the original state before the action was processed, use listenerApi.getOriginalState().

Exactly one action matching option required

When calling startListening or stopListening, you must provide exactly one of the four action matching options: type, actionCreator, matcher, or predicate. Providing zero or more than one will result in incorrect behavior. Every dispatched action is checked against each listener to determine if it should run based on the comparison option provided.

Predicate option allows state-only matching

The predicate option allows matching solely against state-related checks, independent of the actual action. For example, you can trigger logic when a specific state field changes (by comparing currentState and previousState) or when the state matches particular criteria, regardless of which action was dispatched.

Duplicate listener detection

If you try to add a listener entry but another entry with the exact same function reference for the listener and comparison option (actionCreator/matcher/predicate or type string) already exists, no new entry will be added. Instead, the existing unsubscribe method will be returned.

unsubscribe does not cancel active instances by default

When calling unsubscribe() or removeListener(), active running instances are not cancelled by default. To cancel active instances, pass {cancelActive: true} option to the unsubscribe call.

Listener middleware organizational patterns

There are three patterns for organizing listeners: 1) Import effect callbacks from slice files into the listener middleware file and add listeners there; 2) Have slice files import the middleware and directly add their listeners; 3) Create a setup function in the slice but let the listener file call it on startup. The first pattern is simplest and mirrors store setup. Choose based on preference and app structure.

Listener middleware in separate file best practice

It is best to create the listener middleware in a separate file (such as app/listenerMiddleware.ts) rather than in the same file as the store. This avoids potential circular import problems from other files trying to import middleware.addListener.

Fork executor function signature

fork() accepts an executor function that can be either sync or async and receives a forkApi parameter. The executor has signature ForkedTaskExecutor<T> which is (forkApi: ForkedTaskAPI) => T | Promise<T>. The executor can return a value that will be available in the TaskResult.

createListenerMiddleware provides reactive logic similar to sagas

createListenerMiddleware is a middleware that listens for specific actions and runs effect callbacks in response. It provides a listenerApi object with methods like delay() and dispatch() to handle asynchronous reactive logic as an alternative to redux-saga or redux-observable.

createListenerMiddleware startListening accepts actionCreator or predicate matchers

The startListening function returned from createListenerMiddleware accepts options including an actionCreator property to match a specific generated action creator, or other matching predicates. The effect callback function receives the action and a listenerApi object.

createListenerMiddleware should be prepended to middleware chain

When adding createListenerMiddleware to configureStore, the listener middleware should be prepended before other middleware like thunk or dev checks using the prepend() method on the getDefaultMiddleware result.

createListenerMiddleware setup pattern

Import createListenerMiddleware from '@reduxjs/toolkit'. Create an instance with const listenerMiddleware = createListenerMiddleware(). Add it to configureStore's middleware array using getDefaultMiddleware().prepend(listenerMiddleware.middleware) to ensure it runs before serializability checks. Export a typed startAppListening function by calling listenerMiddleware.startListening.withTypes<RootState, AppDispatch>().

Give your agent this brain