Skip to content

Commit 289ae45

Browse files
authored
Merge pull request #3760 from reduxjs/middleware-callback
2 parents a98875c + d5f916b commit 289ae45

12 files changed

+134
-124
lines changed

docs/api/actionCreatorMiddleware.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,6 @@ const actionCreatorMiddleware = createActionCreatorInvariantMiddleware({
6363

6464
const store = configureStore({
6565
reducer,
66-
middleware: new Tuple(actionCreatorMiddleware),
66+
middleware: () => new Tuple(actionCreatorMiddleware),
6767
})
6868
```

docs/api/configureStore.mdx

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -100,18 +100,15 @@ If it is an object of slice reducers, like `{users : usersReducer, posts : posts
100100

101101
### `middleware`
102102

103-
An optional array of Redux middleware functions, or a callback to customise the array of middleware.
103+
A callback which will receive `getDefaultMiddleware` as its argument,
104+
and should return a middleware array.
104105

105-
If this option is provided, it should contain all the middleware functions you
106+
If this option is provided, it should return all the middleware functions you
106107
want added to the store. `configureStore` will automatically pass those to `applyMiddleware`.
107108

108109
If not provided, `configureStore` will call `getDefaultMiddleware` and use the
109110
array of middleware functions it returns.
110111

111-
Where you wish to add onto or customize the default middleware,
112-
you may pass a callback function that will receive `getDefaultMiddleware` as its argument,
113-
and should return a middleware array.
114-
115112
For more details on how the `middleware` parameter works and the list of middleware that are added by default, see the
116113
[`getDefaultMiddleware` docs page](./getDefaultMiddleware.mdx).
117114

@@ -123,7 +120,7 @@ import { configureStore, Tuple } from '@reduxjs/toolkit'
123120
124121
configureStore({
125122
reducer: rootReducer,
126-
middleware: new Tuple(additionalMiddleware, logger),
123+
middleware: () => new Tuple(additionalMiddleware, logger),
127124
})
128125
```
129126

@@ -178,11 +175,6 @@ If you provide an array, this `applyMiddleware` enhancer will _not_ be used.
178175
`configureStore` will warn in console if any middleware are provided (or left as default) but not included in the final list of enhancers.
179176
180177
```ts no-transpile
181-
// warns - middleware left as default but not included in final enhancers
182-
configureStore({
183-
reducer,
184-
enhancers: [offline(offlineConfig)],
185-
})
186178
// warns - middleware customised but not included in final enhancers
187179
configureStore({
188180
reducer,
@@ -199,8 +191,8 @@ configureStore({
199191
// also allowed
200192
configureStore({
201193
reducer,
202-
middleware: [],
203-
enhancers: [offline(offlineConfig)],
194+
middleware: () => [],
195+
enhancers: () => [offline(offlineConfig)],
204196
})
205197
```
206198

@@ -209,20 +201,22 @@ configureStore({
209201
:::note Tuple
210202
Typescript users are required to use a `Tuple` instance (if not using a `getDefaultEnhancer` result, which is already a `Tuple`), for better inference.
211203

204+
```
212205
import { configureStore, Tuple } from '@reduxjs/toolkit'
213206

214207
configureStore({
215208
reducer: rootReducer,
216-
enhancers: new Tuple(offline),
209+
enhancers: () => new Tuple(offline),
217210
})
218211

219-
````
212+
```
220213
221214
Javascript-only users are free to use a plain array if preferred.
222215
223216
:::
224217
225218
## Usage
219+
226220
### Basic Example
227221
228222
```ts
@@ -238,7 +232,7 @@ import rootReducer from './reducers'
238232

239233
const store = configureStore({ reducer: rootReducer })
240234
// The store now has redux-thunk added and the Redux DevTools Extension is turned on
241-
````
235+
```
242236
243237
### Full Example
244238

docs/api/getDefaultMiddleware.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ If you want to customize the list of middleware, you can supply an array of midd
2828
```js
2929
const store = configureStore({
3030
reducer: rootReducer,
31-
middleware: new Tuple(thunk, logger),
31+
middleware: () => new Tuple(thunk, logger),
3232
})
3333

3434
// Store specifically has the thunk and logger middleware applied

docs/api/immutabilityMiddleware.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ const immutableInvariantMiddleware = createImmutableStateInvariantMiddleware({
8686
const store = configureStore({
8787
reducer: exampleSliceReducer,
8888
// Note that this will replace all default middleware
89-
middleware: new Tuple(immutableInvariantMiddleware),
89+
middleware: () => new Tuple(immutableInvariantMiddleware),
9090
})
9191
```
9292

docs/api/serializabilityMiddleware.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ const serializableMiddleware = createSerializableStateInvariantMiddleware({
111111

112112
const store = configureStore({
113113
reducer,
114-
middleware: new Tuple(serializableMiddleware),
114+
middleware: () => new Tuple(serializableMiddleware),
115115
})
116116
```
117117

docs/usage/usage-with-typescript.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ import { configureStore, Tuple } from '@reduxjs/toolkit'
145145

146146
configureStore({
147147
reducer: rootReducer,
148-
middleware: new Tuple(additionalMiddleware, logger),
148+
middleware: () => new Tuple(additionalMiddleware, logger),
149149
})
150150
```
151151

errors.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,6 @@
2929
"27": "Warning: Middleware for RTK-Query API at reducerPath \"\" has not been added to the store.\n You must add the middleware for RTK-Query to function correctly!",
3030
"28": "Cannot refetch a query that has not been started yet.",
3131
"29": "`builder.addCase` cannot be called with an empty action type",
32-
"30": "`builder.addCase` cannot be called with two reducers for the same action type"
32+
"30": "`builder.addCase` cannot be called with two reducers for the same action type",
33+
"31": "\"middleware\" field must be a callback"
3334
}

packages/toolkit/src/configureStore.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export interface ConfigureStoreOptions<
5353
* @example `middleware: (gDM) => gDM().concat(logger, apiMiddleware, yourCustomMiddleware)`
5454
* @see https://redux-toolkit.js.org/api/getDefaultMiddleware#intended-usage
5555
*/
56-
middleware?: ((getDefaultMiddleware: GetDefaultMiddleware<S>) => M) | M
56+
middleware?: (getDefaultMiddleware: GetDefaultMiddleware<S>) => M
5757

5858
/**
5959
* Whether to enable Redux DevTools integration. Defaults to `true`.
@@ -121,7 +121,7 @@ export function configureStore<
121121

122122
const {
123123
reducer = undefined,
124-
middleware = getDefaultMiddleware(),
124+
middleware,
125125
devTools = true,
126126
preloadedState = undefined,
127127
enhancers = undefined,
@@ -139,15 +139,21 @@ export function configureStore<
139139
)
140140
}
141141

142-
let finalMiddleware = middleware
143-
if (typeof finalMiddleware === 'function') {
144-
finalMiddleware = finalMiddleware(getDefaultMiddleware)
142+
if (!IS_PRODUCTION && middleware && typeof middleware !== 'function') {
143+
throw new Error('"middleware" field must be a callback')
144+
}
145+
146+
let finalMiddleware: Tuple<Middlewares<S>>
147+
if (typeof middleware === 'function') {
148+
finalMiddleware = middleware(getDefaultMiddleware)
145149

146150
if (!IS_PRODUCTION && !Array.isArray(finalMiddleware)) {
147151
throw new Error(
148152
'when using a middleware builder function, an array of middleware must be returned'
149153
)
150154
}
155+
} else {
156+
finalMiddleware = getDefaultMiddleware()
151157
}
152158
if (
153159
!IS_PRODUCTION &&

packages/toolkit/src/tests/configureStore.test.ts

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ describe('configureStore', async () => {
110110
describe('given no middleware', () => {
111111
it('calls createStore without any middleware', () => {
112112
expect(
113-
configureStore({ middleware: new Tuple(), reducer })
113+
configureStore({ middleware: () => new Tuple(), reducer })
114114
).toBeInstanceOf(Object)
115115
expect(redux.applyMiddleware).toHaveBeenCalledWith()
116116
expect(mockDevtoolsCompose).toHaveBeenCalled() // @remap-prod-remove-line-line
@@ -122,6 +122,15 @@ describe('configureStore', async () => {
122122
})
123123
})
124124

125+
describe('given an array of middleware', () => {
126+
it('throws an error requiring a callback', () => {
127+
// @ts-expect-error
128+
expect(() => configureStore({ middleware: [], reducer })).toThrow(
129+
'"middleware" field must be a callback'
130+
)
131+
})
132+
})
133+
125134
describe('given undefined middleware', () => {
126135
it('calls createStore with default middleware', () => {
127136
expect(configureStore({ middleware: undefined, reducer })).toBeInstanceOf(
@@ -162,20 +171,12 @@ describe('configureStore', async () => {
162171
})
163172
})
164173

165-
describe('given custom middleware that contains non-functions', () => {
166-
it('throws an error', () => {
167-
expect(() =>
168-
configureStore({ middleware: [true] as any, reducer })
169-
).toThrow('each middleware provided to configureStore must be a function')
170-
})
171-
})
172-
173174
describe('given custom middleware', () => {
174175
it('calls createStore with custom middleware and without default middleware', () => {
175176
const thank: Redux.Middleware = (_store) => (next) => (action) =>
176177
next(action)
177178
expect(
178-
configureStore({ middleware: new Tuple(thank), reducer })
179+
configureStore({ middleware: () => new Tuple(thank), reducer })
179180
).toBeInstanceOf(Object)
180181
expect(redux.applyMiddleware).toHaveBeenCalledWith(thank)
181182
expect(mockDevtoolsCompose).toHaveBeenCalled() // @remap-prod-remove-line-line
@@ -330,7 +331,7 @@ describe('configureStore', async () => {
330331
it("doesn't warn when middleware enhancer is excluded if no middlewares provided", () => {
331332
const store = configureStore({
332333
reducer,
333-
middleware: new Tuple(),
334+
middleware: () => new Tuple(),
334335
enhancers: () => new Tuple(dummyEnhancer),
335336
})
336337

packages/toolkit/src/tests/configureStore.typetest.ts

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -74,19 +74,19 @@ const _anyMiddleware: any = () => () => () => {}
7474

7575
configureStore({
7676
reducer: () => 0,
77-
middleware: new Tuple(middleware),
77+
middleware: () => new Tuple(middleware),
7878
})
7979

8080
configureStore({
8181
reducer: () => 0,
8282
// @ts-expect-error
83-
middleware: [middleware],
83+
middleware: () => [middleware],
8484
})
8585

8686
configureStore({
8787
reducer: () => 0,
8888
// @ts-expect-error
89-
middleware: new Tuple('not middleware'),
89+
middleware: () => new Tuple('not middleware'),
9090
})
9191
}
9292

@@ -520,7 +520,7 @@ const _anyMiddleware: any = () => () => () => {}
520520
{
521521
const store = configureStore({
522522
reducer: reducerA,
523-
middleware: new Tuple(),
523+
middleware: () => new Tuple(),
524524
})
525525
// @ts-expect-error
526526
store.dispatch(thunkA())
@@ -533,7 +533,7 @@ const _anyMiddleware: any = () => () => () => {}
533533
{
534534
const store = configureStore({
535535
reducer: reducerA,
536-
middleware: new Tuple(thunk as ThunkMiddleware<StateA>),
536+
middleware: () => new Tuple(thunk as ThunkMiddleware<StateA>),
537537
})
538538
store.dispatch(thunkA())
539539
// @ts-expect-error
@@ -545,9 +545,8 @@ const _anyMiddleware: any = () => () => () => {}
545545
{
546546
const store = configureStore({
547547
reducer: reducerA,
548-
middleware: new Tuple(
549-
0 as unknown as Middleware<(a: StateA) => boolean, StateA>
550-
),
548+
middleware: () =>
549+
new Tuple(0 as unknown as Middleware<(a: StateA) => boolean, StateA>),
551550
})
552551
const result: boolean = store.dispatch(5)
553552
// @ts-expect-error
@@ -566,7 +565,7 @@ const _anyMiddleware: any = () => () => () => {}
566565
>
567566
const store = configureStore({
568567
reducer: reducerA,
569-
middleware,
568+
middleware: () => middleware,
570569
})
571570

572571
const result: 'A' = store.dispatch('a')

0 commit comments

Comments
 (0)