Skip to main content

createDynamicMiddleware

Overview

A "meta-middleware" that allows adding middleware to the dispatch chain after store initialisation.

Instance Creation

import { createDynamicMiddleware, configureStore } from '@reduxjs/toolkit'

const dynamicMiddleware = createDynamicMiddleware()

const store = configureStore({
reducer: {
todos: todosReducer,
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().prepend(dynamicMiddleware.middleware),
})
tip

It's possible to pass two type parameters to createDynamicMiddleware, State and Dispatch.

These are used by methods that receive middleware to ensure that the provided middleware are compatible with the types provided.

const dynamicMiddleware = createDynamicMiddleware<State, Dispatch>()

However, if these values are derived from the store (as they should be), a circular type dependency is formed.

As a result, it's better to use the withTypes helper attached to addMiddleware, withMiddleware and createDispatchWithMiddlewareHook.

import { createDynamicMiddleware } from '@reduxjs/toolkit/react'
import type { RootState, AppDispatch } from './store'

const dynamicMiddleware = createDynamicMiddleware()

const {
middleware,
addMiddleware,
withMiddleware,
createDispatchWithMiddlewareHook,
} = dynamicMiddleware

interface MiddlewareApiConfig {
state: RootState
dispatch: AppDispatch
}

export const addAppMiddleware = addMiddleware.withTypes<MiddlewareApiConfig>()

export const withAppMiddleware = withMiddleware.withTypes<MiddlewareApiConfig>()

export const createAppDispatchWithMiddlewareHook =
createDispatchWithMiddlewareHook.withTypes<MiddlewareApiConfig>()

export default middleware

Dynamic Middleware Instance

The "dynamic middleware instance" returned from createDynamicMiddleware is an object similar to the object generated by createListenerMiddleware. The instance object is not the actual Redux middleware itself. Rather, it contains the middleware and some instance methods used to add middleware to the chain.

export type DynamicMiddlewareInstance<
State = unknown,
Dispatch extends ReduxDispatch<UnknownAction> = ReduxDispatch<UnknownAction>,
> = {
middleware: DynamicMiddleware<State, Dispatch>
addMiddleware: AddMiddleware<State, Dispatch>
withMiddleware: WithMiddleware<State, Dispatch>
}

middleware

The wrapper middleware instance, to add to the Redux store.

You can place this anywhere in the middleware chain, but note that all the middleware you inject into this instance will be contained within this position.

addMiddleware

Injects a set of middleware into the instance.

addMiddleware(logger, listenerMiddleware.instance)
note
  • Middleware are compared by function reference, and each is only added to the chain once.

  • Middleware are stored in an ES6 map, and are thus called in insertion order during dispatch.

withMiddleware

Accepts a set of middleware, and creates an action. When dispatched, it injects the middleware and returns a version of dispatch typed to be aware of any extensions added.

const listenerDispatch = store.dispatch(
withMiddleware(listenerMiddleware.middleware),
)

const unsubscribe = listenerDispatch(addListener({ type, effect }))

React Integration

When imported from the React-specific entry point (@reduxjs/toolkit/react), the result of calling createDynamicMiddleware will have extra methods attached.

These depend on having react-redux installed.

interface ReactDynamicMiddlewareInstance<
State = any,
Dispatch extends ReduxDispatch<UnknownAction> = ReduxDispatch<UnknownAction>,
> extends DynamicMiddlewareInstance<State, Dispatch> {
createDispatchWithMiddlewareHook: CreateDispatchWithMiddlewareHook<
State,
Dispatch
>
createDispatchWithMiddlewareHookFactory: (
context?: Context<
ReactReduxContextValue<State, ActionFromDispatch<Dispatch>>
>,
) => CreateDispatchWithMiddlewareHook<State, Dispatch>
}

createDispatchWithMiddlewareHook

Accepts a set of middleware, and returns a useDispatch hook returning a dispatch typed to include extensions from provided middleware.

const useListenerDispatch = createDispatchWithMiddlewareHook(
listenerInstance.middleware,
)

const Component = () => {
const listenerDispatch = useListenerDispatch()
useEffect(() => {
const unsubscribe = listenerDispatch(addListener({ type, effect }))
return () => unsubscribe()
}, [dispatch])
}
caution

Middleware is injected when createDispatchWithMiddlewareHook is called, not when the useDispatch hook is used.

createDispatchWithMiddlewareHookFactory

Accepts a React context instance, and returns a createDispatchWithMiddlewareHook built to use that context.

const createDispatchWithMiddlewareHook =
createDispatchWithMiddlewareHookFactory(context)

Useful if you're using a custom context for React Redux.