Skip to content

Commit 75307e9

Browse files
committed
Merge branch 'v2.0-integration' into entity-record
2 parents 7ed2439 + a2f3c9a commit 75307e9

File tree

87 files changed

+3209
-803
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

87 files changed

+3209
-803
lines changed

docs/api/actionCreatorMiddleware.mdx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ export default function (state = {}, action: any) {
4747
import {
4848
configureStore,
4949
createActionCreatorInvariantMiddleware,
50+
Tuple,
5051
} from '@reduxjs/toolkit'
5152
import reducer from './reducer'
5253

@@ -62,6 +63,6 @@ const actionCreatorMiddleware = createActionCreatorInvariantMiddleware({
6263

6364
const store = configureStore({
6465
reducer,
65-
middleware: [actionCreatorMiddleware],
66+
middleware: new Tuple(actionCreatorMiddleware),
6667
})
6768
```

docs/api/autoBatchEnhancer.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,9 @@ const { incrementBatched, decrementUnbatched } = counterSlice.actions
5151
const store = configureStore({
5252
reducer: counterSlice.reducer,
5353
// highlight-start
54-
enhancers: (existingEnhancers) => {
54+
enhancers: (getDefaultEnhancers) => {
5555
// Add the autobatch enhancer to the store setup
56-
return existingEnhancers.concat(autoBatchEnhancer())
56+
return getDefaultEnhancers().concat(autoBatchEnhancer())
5757
},
5858
// highlight-end
5959
})

docs/api/configureStore.mdx

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,12 @@ to the store setup for a better development experience.
1717
`configureStore` accepts a single configuration object parameter, with the following options:
1818

1919
```ts no-transpile
20-
type ConfigureEnhancersCallback = (
21-
defaultEnhancers: EnhancerArray<[StoreEnhancer]>
22-
) => StoreEnhancer[]
2320

2421
interface ConfigureStoreOptions<
2522
S = any,
2623
A extends Action = AnyAction,
2724
M extends Middlewares<S> = Middlewares<S>
25+
E extends Enhancers = Enhancers
2826
> {
2927
/**
3028
* A single reducer function that will be used as the root reducer, or an
@@ -59,11 +57,11 @@ interface ConfigureStoreOptions<
5957
* The store enhancers to apply. See Redux's `createStore()`.
6058
* All enhancers will be included before the DevTools Extension enhancer.
6159
* If you need to customize the order of enhancers, supply a callback
62-
* function that will receive the original array (ie, `[applyMiddleware]`),
63-
* and should return a new array (such as `[applyMiddleware, offline]`).
60+
* function that will receive the getDefaultEnhancers,
61+
* and should return a new array (such as `getDefaultEnhancers().concat(offline)`).
6462
* If you only need to add middleware, you can use the `middleware` parameter instead.
6563
*/
66-
enhancers?: StoreEnhancer[] | ConfigureEnhancersCallback
64+
enhancers?: (getDefaultEnhancers: GetDefaultEnhancers<M>) => E | E
6765
}
6866

6967
function configureStore<S = any, A extends Action = AnyAction>(
@@ -96,6 +94,22 @@ and should return a middleware array.
9694
For more details on how the `middleware` parameter works and the list of middleware that are added by default, see the
9795
[`getDefaultMiddleware` docs page](./getDefaultMiddleware.mdx).
9896

97+
:::note Tuple
98+
Typescript users are required to use a `Tuple` instance (if not using a `getDefaultMiddleware` result, which is already a `Tuple`), for better inference.
99+
100+
```ts no-transpile
101+
import { configureStore, Tuple } from '@reduxjs/toolkit'
102+
103+
configureStore({
104+
reducer: rootReducer,
105+
middleware: new Tuple(additionalMiddleware, logger),
106+
})
107+
```
108+
109+
Javascript-only users are free to use a plain array if preferred.
110+
111+
:::
112+
99113
### `devTools`
100114
101115
If this is a boolean, it will be used to indicate whether `configureStore` should automatically enable support for [the Redux DevTools browser extension](https://github.com/reduxjs/redux-devtools).
@@ -124,7 +138,7 @@ If defined as an array, these will be passed to [the Redux `compose` function](h
124138
125139
This should _not_ include `applyMiddleware()` or the Redux DevTools Extension `composeWithDevTools`, as those are already handled by `configureStore`.
126140
127-
Example: `enhancers: [offline]` will result in a final setup of `[applyMiddleware, offline, devToolsExtension]`.
141+
Example: `enhancers: new Tuple(offline)` will result in a final setup of `[applyMiddleware, offline, devToolsExtension]`.
128142
129143
If defined as a callback function, it will be called with the existing array of enhancers _without_ the DevTools Extension (currently `[applyMiddleware]`),
130144
and should return a new array of enhancers. This is primarily useful for cases where a store enhancer needs to be added
@@ -133,6 +147,22 @@ in front of `applyMiddleware`, such as `redux-first-router` or `redux-offline`.
133147
Example: `enhancers: (defaultEnhancers) => defaultEnhancers.prepend(offline)` will result in a final setup
134148
of `[offline, applyMiddleware, devToolsExtension]`.
135149
150+
:::note Tuple
151+
Typescript users are required to use a `Tuple` instance (if not using a `getDefaultEnhancer` result, which is already a `Tuple`), for better inference.
152+
153+
```ts no-transpile
154+
import { configureStore, Tuple } from '@reduxjs/toolkit'
155+
156+
configureStore({
157+
reducer: rootReducer,
158+
enhancers: new Tuple(offline),
159+
})
160+
```
161+
162+
Javascript-only users are free to use a plain array if preferred.
163+
164+
:::
165+
136166
## Usage
137167
138168
### Basic Example
@@ -203,7 +233,10 @@ const store = configureStore({
203233
middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(logger),
204234
devTools: process.env.NODE_ENV !== 'production',
205235
preloadedState,
206-
enhancers: [batchedSubscribe(debounceNotify)],
236+
enhancers: (getDefaultEnhancers) =>
237+
getDefaultEnhancers({
238+
autoBatch: false,
239+
}).concat(batchedSubscribe(debounceNotify)),
207240
})
208241

209242
// The store has been created with these options:

docs/api/createAction.mdx

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ const action = increment(3)
3131
// { type: 'counter/increment', payload: 3 }
3232
```
3333
34-
The `createAction` helper combines these two declarations into one. It takes an action type and returns an action creator for that type. The action creator can be called either without arguments or with a `payload` to be attached to the action. Also, the action creator overrides [toString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString) so that the action type becomes its string representation.
34+
The `createAction` helper combines these two declarations into one. It takes an action type and returns an action creator for that type. The action creator can be called either without arguments or with a `payload` to be attached to the action.
3535
3636
```ts
3737
import { createAction } from '@reduxjs/toolkit'
@@ -44,10 +44,7 @@ let action = increment()
4444
action = increment(3)
4545
// returns { type: 'counter/increment', payload: 3 }
4646

47-
console.log(increment.toString())
48-
// 'counter/increment'
49-
50-
console.log(`The action type is: ${increment}`)
47+
console.log(`The action type is: ${increment.type}`)
5148
// 'The action type is: counter/increment'
5249
```
5350
@@ -89,7 +86,7 @@ If provided, all arguments from the action creator will be passed to the prepare
8986
9087
## Usage with createReducer()
9188
92-
Because of their `toString()` override, action creators returned by `createAction()` can be used directly as keys for the case reducers passed to [createReducer()](createReducer.mdx).
89+
Action creators can be passed directly to `addCase` in a [createReducer()](createReducer.mdx) build callback.
9390
9491
```ts
9592
import { createAction, createReducer } from '@reduxjs/toolkit'
@@ -103,21 +100,23 @@ const counterReducer = createReducer(0, (builder) => {
103100
})
104101
```
105102
103+
<!-- TODO: how do we handle this? -->
104+
106105
## Non-String Action Types
107106
108107
In principle, Redux lets you use any kind of value as an action type. Instead of strings, you could theoretically use numbers, [symbols](https://developer.mozilla.org/en-US/docs/Glossary/Symbol), or anything else ([although it's recommended that the value should at least be serializable](https://redux.js.org/faq/actions#why-should-type-be-a-string-or-at-least-serializable-why-should-my-action-types-be-constants)).
109108
110-
However, Redux Toolkit rests on the assumption that you use string action types. Specifically, some of its features rely on the fact that with strings, the `toString()` method of an `createAction()` action creator returns the matching action type. This is not the case for non-string action types because `toString()` will return the string-converted type value rather than the type itself.
109+
However, Redux Toolkit rests on the assumption that you use string action types.
111110
112111
```js
113112
const INCREMENT = Symbol('increment')
114113
const increment = createAction(INCREMENT)
115114

116-
increment.toString()
115+
increment.type.toString()
117116
// returns the string 'Symbol(increment)',
118117
// not the INCREMENT symbol itself
119118

120-
increment.toString() === INCREMENT
119+
increment.type.toString() === INCREMENT
121120
// false
122121
```
123122

docs/api/createDynamicMiddleware.mdx

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
---
2+
id: createDynamicMiddleware
3+
title: createDynamicMiddleware
4+
sidebar_label: createDynamicMiddleware
5+
hide_title: true
6+
---
7+
8+
&nbsp;
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

Comments
 (0)