|
| 1 | +--- |
| 2 | +id: createDynamicMiddleware |
| 3 | +title: createDynamicMiddleware |
| 4 | +sidebar_label: createDynamicMiddleware |
| 5 | +hide_title: true |
| 6 | +--- |
| 7 | + |
| 8 | + |
| 9 | + |
| 10 | +# `createDynamicMiddleware` |
| 11 | + |
| 12 | +## Overview |
| 13 | + |
| 14 | +A "meta-middleware" that allows adding middleware to the dispatch chain after store initialisation. |
| 15 | + |
| 16 | +## Instance Creation |
| 17 | + |
| 18 | +```ts no-transpile |
| 19 | +import { createDynamicMiddleware, configureStore } from '@reduxjs/toolkit' |
| 20 | + |
| 21 | +const dynamicMiddleware = createDynamicMiddleware() |
| 22 | + |
| 23 | +const store = configureStore({ |
| 24 | + reducer: { |
| 25 | + todos: todosReducer, |
| 26 | + }, |
| 27 | + middleware: (getDefaultMiddleware) => |
| 28 | + getDefaultMiddleware().prepend(dynamicMiddleware.middleware), |
| 29 | +}) |
| 30 | +``` |
| 31 | + |
| 32 | +:::tip |
| 33 | + |
| 34 | +It's possible to pass two type parameters to `createDynamicMiddleware`, `State` and `Dispatch`. |
| 35 | + |
| 36 | +These are used by methods that receive middleware to ensure that the provided middleware are compatible with the types provided. |
| 37 | + |
| 38 | +```ts no-transpile |
| 39 | +const dynamicMiddleware = createDynamicMiddleware<State, Dispatch>() |
| 40 | +``` |
| 41 | + |
| 42 | +However, if these values are derived from the store (as they should be), a circular type dependency is formed. |
| 43 | + |
| 44 | +As a result, it's better to use the `withTypes` helper attached to `addMiddleware`, `withMiddleware` and `createDispatchWithMiddlewareHook`. |
| 45 | + |
| 46 | +```ts no-transpile |
| 47 | +import { createDynamicMiddleware } from '@reduxjs/toolkit/react' |
| 48 | +import type { RootState, AppDispatch } from './store' |
| 49 | + |
| 50 | +const dynamicMiddleware = createDynamicMiddleware() |
| 51 | + |
| 52 | +const { |
| 53 | + middleware, |
| 54 | + addMiddleware, |
| 55 | + withMiddleware, |
| 56 | + createDispatchWithMiddlewareHook, |
| 57 | +} = dynamicMiddleware |
| 58 | + |
| 59 | +interface MiddlewareApiConfig { |
| 60 | + state: RootState |
| 61 | + dispatch: AppDispatch |
| 62 | +} |
| 63 | + |
| 64 | +export const addAppMiddleware = addMiddleware.withTypes<MiddlewareApiConfig>() |
| 65 | + |
| 66 | +export const withAppMiddleware = withMiddleware.withTypes<MiddlewareApiConfig>() |
| 67 | + |
| 68 | +export const createAppDispatchWithMiddlewareHook = |
| 69 | + createDispatchWithMiddlewareHook.withTypes<MiddlewareApiConfig>() |
| 70 | + |
| 71 | +export default middleware |
| 72 | +``` |
| 73 | + |
| 74 | +::: |
| 75 | + |
| 76 | +## Dynamic Middleware Instance |
| 77 | + |
| 78 | +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. |
| 79 | + |
| 80 | +```ts no-transpile |
| 81 | +export type DynamicMiddlewareInstance< |
| 82 | + State = unknown, |
| 83 | + Dispatch extends ReduxDispatch<AnyAction> = ReduxDispatch<AnyAction> |
| 84 | +> = { |
| 85 | + middleware: DynamicMiddleware<State, Dispatch> |
| 86 | + addMiddleware: AddMiddleware<State, Dispatch> |
| 87 | + withMiddleware: WithMiddleware<State, Dispatch> |
| 88 | +} |
| 89 | +``` |
| 90 | +
|
| 91 | +### `middleware` |
| 92 | +
|
| 93 | +The wrapper middleware instance, to add to the Redux store. |
| 94 | +
|
| 95 | +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. |
| 96 | +
|
| 97 | +### `addMiddleware` |
| 98 | +
|
| 99 | +Injects a set of middleware into the instance. |
| 100 | +
|
| 101 | +```ts no-transpile |
| 102 | +addMiddleware(logger, listenerMiddleware.instance) |
| 103 | +``` |
| 104 | +
|
| 105 | +:::note |
| 106 | +
|
| 107 | +- Middleware are compared by function reference, and each is only added to the chain once. |
| 108 | +
|
| 109 | +- Middleware are stored in an ES6 map, and are thus called in insertion order during dispatch. |
| 110 | +
|
| 111 | +::: |
| 112 | +
|
| 113 | +### `withMiddleware` |
| 114 | +
|
| 115 | +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. |
| 116 | +
|
| 117 | +```ts no-transpile |
| 118 | +const listenerDispatch = store.dispatch( |
| 119 | + withMiddleware(listenerMiddleware.middleware) |
| 120 | +) |
| 121 | + |
| 122 | +const unsubscribe = listenerDispatch(addListener({ type, effect })) |
| 123 | +``` |
| 124 | + |
| 125 | +## React Integration |
| 126 | + |
| 127 | +When imported from the React-specific entry point (`@reduxjs/toolkit/react`), the result of calling `createDynamicMiddleware` will have extra methods attached. |
| 128 | + |
| 129 | +_These depend on having `react-redux` installed._ |
| 130 | + |
| 131 | +```ts no-transpile |
| 132 | +interface ReactDynamicMiddlewareInstance< |
| 133 | + State = any, |
| 134 | + Dispatch extends ReduxDispatch<AnyAction> = ReduxDispatch<AnyAction> |
| 135 | +> extends DynamicMiddlewareInstance<State, Dispatch> { |
| 136 | + createDispatchWithMiddlewareHook: CreateDispatchWithMiddlewareHook< |
| 137 | + State, |
| 138 | + Dispatch |
| 139 | + > |
| 140 | + createDispatchWithMiddlewareHookFactory: ( |
| 141 | + context?: Context< |
| 142 | + ReactReduxContextValue<State, ActionFromDispatch<Dispatch>> |
| 143 | + > |
| 144 | + ) => CreateDispatchWithMiddlewareHook<State, Dispatch> |
| 145 | +} |
| 146 | +``` |
| 147 | + |
| 148 | +### `createDispatchWithMiddlewareHook` |
| 149 | + |
| 150 | +Accepts a set of middleware, and returns a [`useDispatch`](https://react-redux.js.org/api/hooks#usedispatch) hook returning a `dispatch` typed to include extensions from provided middleware. |
| 151 | + |
| 152 | +```ts no-transpile |
| 153 | +const useListenerDispatch = createDispatchWithMiddlewareHook( |
| 154 | + listenerMiddleware.instance |
| 155 | +) |
| 156 | + |
| 157 | +const Component = () => { |
| 158 | + const listenerDispatch = useListenerDispatch() |
| 159 | + useEffect(() => { |
| 160 | + const unsubscribe = listenerDispatch(addListener({ type, effect })) |
| 161 | + return () => unsubscribe() |
| 162 | + }, [dispatch]) |
| 163 | +} |
| 164 | +``` |
| 165 | + |
| 166 | +:::caution |
| 167 | + |
| 168 | +Middleware is injected when `createDispatchWithMiddlewareHook` is called, not when the `useDispatch` hook is used. |
| 169 | + |
| 170 | +::: |
| 171 | + |
| 172 | +### `createDispatchWithMiddlewareHookFactory` |
| 173 | + |
| 174 | +Accepts a React context instance, and returns a `createDispatchWithMiddlewareHook` built to use that context. |
| 175 | + |
| 176 | +```ts no-transpile |
| 177 | +const createDispatchWithMiddlewareHook = |
| 178 | + createDispatchWithMiddlewareHookFactory(context) |
| 179 | +``` |
| 180 | + |
| 181 | +Useful if you're using a [custom context](https://react-redux.js.org/using-react-redux/accessing-store#providing-custom-context) for React Redux. |
0 commit comments