Skip to content

Commit

Permalink
Merge pull request #3292 from EskiMojo14/dynamic-middleware-2
Browse files Browse the repository at this point in the history
  • Loading branch information
markerikson authored May 16, 2023
2 parents 5845499 + 7d18ae1 commit 3a8686e
Show file tree
Hide file tree
Showing 19 changed files with 1,270 additions and 15 deletions.
181 changes: 181 additions & 0 deletions docs/api/createDynamicMiddleware.mdx
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.
Loading

0 comments on commit 3a8686e

Please sign in to comment.