-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #3292 from EskiMojo14/dynamic-middleware-2
- Loading branch information
Showing
19 changed files
with
1,270 additions
and
15 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,181 @@ | ||
--- | ||
id: createDynamicMiddleware | ||
title: createDynamicMiddleware | ||
sidebar_label: createDynamicMiddleware | ||
hide_title: true | ||
--- | ||
|
||
| ||
|
||
# `createDynamicMiddleware` | ||
|
||
## Overview | ||
|
||
A "meta-middleware" that allows adding middleware to the dispatch chain after store initialisation. | ||
|
||
## Instance Creation | ||
|
||
```ts no-transpile | ||
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. | ||
|
||
```ts no-transpile | ||
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`. | ||
|
||
```ts no-transpile | ||
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. | ||
|
||
```ts no-transpile | ||
export type DynamicMiddlewareInstance< | ||
State = unknown, | ||
Dispatch extends ReduxDispatch<AnyAction> = ReduxDispatch<AnyAction> | ||
> = { | ||
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. | ||
```ts no-transpile | ||
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. | ||
```ts no-transpile | ||
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._ | ||
|
||
```ts no-transpile | ||
interface ReactDynamicMiddlewareInstance< | ||
State = any, | ||
Dispatch extends ReduxDispatch<AnyAction> = ReduxDispatch<AnyAction> | ||
> 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`](https://react-redux.js.org/api/hooks#usedispatch) hook returning a `dispatch` typed to include extensions from provided middleware. | ||
|
||
```ts no-transpile | ||
const useListenerDispatch = createDispatchWithMiddlewareHook( | ||
listenerMiddleware.instance | ||
) | ||
|
||
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. | ||
|
||
```ts no-transpile | ||
const createDispatchWithMiddlewareHook = | ||
createDispatchWithMiddlewareHookFactory(context) | ||
``` | ||
|
||
Useful if you're using a [custom context](https://react-redux.js.org/using-react-redux/accessing-store#providing-custom-context) for React Redux. |
Oops, something went wrong.