Skip to content

Commit 65ba8a3

Browse files
committed
Merge branch 'master' of https://github.com/reduxjs/redux-toolkit into feat/use-suspend-all
2 parents 1f17ca7 + 7f76635 commit 65ba8a3

File tree

163 files changed

+9689
-4793
lines changed

Some content is hidden

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

163 files changed

+9689
-4793
lines changed

.github/workflows/test-codegen.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ jobs:
3131

3232
strategy:
3333
matrix:
34-
node-version: [12.x]
34+
node-version: ['16.x']
3535

3636
steps:
3737
- uses: actions/checkout@v2

.github/workflows/tests.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ jobs:
2828
runs-on: ubuntu-latest
2929
strategy:
3030
matrix:
31-
node: ['14.x']
31+
node: ['16.x']
3232

3333
steps:
3434
- name: Checkout repo
@@ -58,7 +58,7 @@ jobs:
5858
strategy:
5959
fail-fast: false
6060
matrix:
61-
node: ['14.x']
61+
node: ['16.x']
6262
steps:
6363
- name: Checkout repo
6464
uses: actions/checkout@v2
@@ -95,7 +95,7 @@ jobs:
9595
strategy:
9696
fail-fast: false
9797
matrix:
98-
node: ['14.x']
98+
node: ['16.x']
9999
ts: ['4.1', '4.2', '4.3', '4.4', '4.5', '4.6', '4.7']
100100
steps:
101101
- name: Checkout repo

.yarn/patches/react-scripts__npm_4.0.2.patch

-28
This file was deleted.

docs/api/configureStore.mdx

+14-11
Original file line numberDiff line numberDiff line change
@@ -98,16 +98,17 @@ For more details on how the `middleware` parameter works and the list of middlew
9898

9999
### `devTools`
100100

101-
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/zalmoxisus/redux-devtools-extension).
101+
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).
102102

103103
If it is an object, then the DevTools Extension will be enabled, and the options object will be passed to `composeWithDevtools()`. See
104-
the DevTools Extension docs for [`EnhancerOptions`](https://github.com/zalmoxisus/redux-devtools-extension/blob/master/docs/API/Arguments.md#windowdevtoolsextensionconfig) for
104+
the DevTools Extension docs for [`EnhancerOptions`](https://github.com/reduxjs/redux-devtools/blob/main/extension/docs/API/Arguments.md) for
105105
a list of the specific options that are available.
106106

107107
Defaults to `true`.
108108

109-
The Redux DevTools Extension recently added [support for showing action stack traces](https://github.com/zalmoxisus/redux-devtools-extension/blob/d4ef75691ad294646f74bca38b973b19850a37cf/docs/Features/Trace.md) that show exactly where each action was dispatched. Capturing the traces can add a bit of overhead, so the DevTools Extension allows users to configure whether action stack traces are captured.
110-
109+
#### `trace`
110+
The Redux DevTools Extension recently added [support for showing action stack traces](https://github.com/reduxjs/redux-devtools/blob/main/extension/docs/Features/Trace.md) that show exactly where each action was dispatched.
111+
Capturing the traces can add a bit of overhead, so the DevTools Extension allows users to configure whether action stack traces are captured by [setting the 'trace' argument](https://github.com/reduxjs/redux-devtools/blob/main/extension/docs/API/Arguments.md#trace).
111112
If the DevTools are enabled by passing `true` or an object, then `configureStore` will default to enabling capturing action stack traces in development mode only.
112113

113114
### `preloadedState`
@@ -137,7 +138,7 @@ of `[offline, applyMiddleware, devToolsExtension]`.
137138

138139
```ts
139140
// file: reducers.ts noEmit
140-
import { Reducer } from '@reduxjs/toolkit'
141+
import type { Reducer } from '@reduxjs/toolkit'
141142
declare const rootReducer: Reducer<{}>
142143
export default rootReducer
143144
@@ -154,12 +155,12 @@ const store = configureStore({ reducer: rootReducer })
154155

155156
```ts no-transpile
156157
// file: todos/todosReducer.ts noEmit
157-
import { Reducer } from '@reduxjs/toolkit'
158+
import type { Reducer } from '@reduxjs/toolkit'
158159
declare const reducer: Reducer<{}>
159160
export default reducer
160161
161162
// file: visibility/visibilityReducer.ts noEmit
162-
import { Reducer } from '@reduxjs/toolkit'
163+
import type { Reducer } from '@reduxjs/toolkit'
163164
declare const reducer: Reducer<{}>
164165
export default reducer
165166
@@ -169,8 +170,8 @@ import { configureStore } from '@reduxjs/toolkit'
169170
// We'll use redux-logger just as an example of adding another middleware
170171
import logger from 'redux-logger'
171172
172-
// And use redux-batch as an example of adding enhancers
173-
import { reduxBatch } from '@manaflair/redux-batch'
173+
// And use redux-batched-subscribe as an example of adding enhancers
174+
import { batchedSubscribe } from 'redux-batched-subscribe'
174175
175176
import todosReducer from './todos/todosReducer'
176177
import visibilityReducer from './visibility/visibilityReducer'
@@ -194,17 +195,19 @@ const preloadedState = {
194195
visibilityFilter: 'SHOW_COMPLETED',
195196
}
196197
198+
const debounceNotify = _.debounce(notify => notify());
199+
197200
const store = configureStore({
198201
reducer,
199202
middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(logger),
200203
devTools: process.env.NODE_ENV !== 'production',
201204
preloadedState,
202-
enhancers: [reduxBatch],
205+
enhancers: [batchedSubscribe(debounceNotify)],
203206
})
204207
205208
// The store has been created with these options:
206209
// - The slice reducers were automatically passed to combineReducers()
207210
// - redux-thunk and redux-logger were added as middleware
208211
// - The Redux DevTools Extension is disabled for production
209-
// - The middleware, batch, and devtools enhancers were composed together
212+
// - The middleware, batched subscribe, and devtools enhancers were composed together
210213
```

docs/api/createAction.mdx

+5-3
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,8 @@ This `match` method is a [TypeScript type guard](https://www.typescriptlang.org/
154154
This behavior can be particularly useful when used in custom middlewares, where manual casts might be neccessary otherwise.
155155
156156
```ts
157-
import { createAction, Action } from '@reduxjs/toolkit'
157+
import { createAction } from '@reduxjs/toolkit'
158+
import type { Action } from '@reduxjs/toolkit'
158159

159160
const increment = createAction<number>('INCREMENT')
160161

@@ -171,8 +172,9 @@ function someFunction(action: Action) {
171172
The `match` method can also be used as a filter method, which makes it powerful when used with redux-observable:
172173
173174
```ts
174-
import { createAction, Action } from '@reduxjs/toolkit'
175-
import { Observable } from 'rxjs'
175+
import { createAction } from '@reduxjs/toolkit'
176+
import type { Action } from '@reduxjs/toolkit'
177+
import type { Observable } from 'rxjs'
176178
import { map, filter } from 'rxjs/operators'
177179

178180
const increment = createAction<number>('INCREMENT')

docs/api/createAsyncThunk.mdx

+44-8
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,8 @@ A real-life example of that would look like this:
399399

400400
```ts no-transpile
401401
// file: store.ts noEmit
402-
import { configureStore, Reducer } from '@reduxjs/toolkit'
402+
import { configureStore } from '@reduxjs/toolkit'
403+
import type { Reducer } from '@reduxjs/toolkit'
403404
import { useDispatch } from 'react-redux'
404405

405406
declare const reducer: Reducer<{}>
@@ -507,15 +508,47 @@ const fetchUserById = createAsyncThunk(
507508
)
508509
```
509510

511+
### Checking if a Promise Rejection was from an Error or Cancellation
512+
513+
To investigate behavior around thunk cancellation, you can inspect various properties on the `meta` object of the dispatched action.
514+
If a thunk was cancelled, the result of the promise will be a `rejected` action (regardless of whether that action was actually dispatched to the store).
515+
516+
- If it was cancelled before execution, `meta.condition` will be true.
517+
- If it was aborted while running, `meta.aborted` will be true.
518+
- If neither of those is true, the thunk was not cancelled, it was simply rejected, either by a Promise rejection or `rejectWithValue`.
519+
- If the thunk was not rejected, both `meta.aborted` and `meta.condition` will be `undefined`.
520+
521+
So if you wanted to test that a thunk was cancelled before executing, you can do the following:
522+
523+
```ts no-transpile
524+
import { createAsyncThunk, isRejected } from '@reduxjs/toolkit'
525+
526+
test('this thunk should always be skipped', async () => {
527+
const thunk = createAsyncThunk(
528+
'users/fetchById',
529+
async () => throw new Error('This promise should never be entered'),
530+
{
531+
condition: () => false,
532+
}
533+
)
534+
const result = await thunk()(dispatch, getState, null)
535+
536+
expect(result.meta.condition).toBe(true)
537+
expect(result.meta.aborted).toBe(false)
538+
})
539+
```
540+
510541
## Examples
511542

512543
- Requesting a user by ID, with loading state, and only one request at a time:
513544

514545
```ts no-transpile
515546
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'
516-
import { userAPI } from './userAPI'
547+
import { userAPI, User } from './userAPI'
517548

518-
const fetchUserById = createAsyncThunk(
549+
const fetchUserById = createAsyncThunk<User, string, {
550+
state: { users: { loading: string, currentRequestId: string } }
551+
}> (
519552
'users/fetchByIdStatus',
520553
async (userId: string, { getState, requestId }) => {
521554
const { currentRequestId, loading } = getState().users
@@ -592,7 +625,8 @@ const UsersComponent = () => {
592625

593626
```ts no-transpile
594627
// file: store.ts noEmit
595-
import { configureStore, Reducer } from '@reduxjs/toolkit'
628+
import { configureStore } from '@reduxjs/toolkit'
629+
import type { Reducer } from '@reduxjs/toolkit'
596630
import { useDispatch } from 'react-redux'
597631
import usersReducer from './user/slice'
598632

@@ -609,7 +643,7 @@ export declare const userAPI: {
609643
// file: user/slice.ts
610644
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'
611645
import { userAPI } from './userAPI'
612-
import { AxiosError } from 'axios'
646+
import type { AxiosError } from 'axios'
613647

614648
// Sample types that will be used
615649
export interface User {
@@ -691,10 +725,12 @@ declare module 'some-toast-library' {
691725
// file: user/UsersComponent.ts
692726

693727
import React from 'react'
694-
import { useAppDispatch, RootState } from '../store'
728+
import { useAppDispatch } from '../store'
729+
import type { RootState } from '../store'
695730
import { useSelector } from 'react-redux'
696-
import { User, updateUser } from './slice'
697-
import { FormikHelpers } from 'formik'
731+
import { updateUser } from './slice'
732+
import type { User } from './slice'
733+
import type { FormikHelpers } from 'formik'
698734
import { showToast } from 'some-toast-library'
699735

700736
interface FormValues extends Omit<User, 'id'> {}

docs/api/createSlice.mdx

+6-3
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ Internally, it uses [`createAction`](./createAction.mdx) and [`createReducer`](.
1818
you may also use [Immer](https://immerjs.github.io/immer/) to write "mutating" immutable updates:
1919

2020
```ts
21-
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
21+
import { createSlice } from '@reduxjs/toolkit'
22+
import type { PayloadAction } from '@reduxjs/toolkit'
2223

2324
interface CounterState {
2425
value: number
@@ -108,7 +109,8 @@ const counterSlice = createSlice({
108109
If you need to customize the creation of the payload value of an action creator by means of a [`prepare callback`](./createAction.mdx#using-prepare-callbacks-to-customize-action-contents), the value of the appropriate field of the `reducers` argument object should be an object instead of a function. This object must contain two properties: `reducer` and `prepare`. The value of the `reducer` field should be the case reducer function while the value of the `prepare` field should be the prepare callback function:
109110

110111
```ts
111-
import { createSlice, PayloadAction, nanoid } from '@reduxjs/toolkit'
112+
import { createSlice, nanoid } from '@reduxjs/toolkit'
113+
import type { PayloadAction } from '@reduxjs/toolkit'
112114
113115
interface Item {
114116
id: string
@@ -224,7 +226,8 @@ for references in a larger codebase.
224226
## Examples
225227

226228
```ts
227-
import { createSlice, createAction, PayloadAction } from '@reduxjs/toolkit'
229+
import { createSlice, createAction } from '@reduxjs/toolkit'
230+
import type { PayloadAction } from '@reduxjs/toolkit'
228231
import { createStore, combineReducers } from 'redux'
229232
230233
const incrementBy = createAction<number>('incrementBy')

docs/api/matching-utilities.mdx

+18-10
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ Accepts the same inputs as `isAllOf` and will return a type guard function that
4848
A higher-order function that returns a type guard function that may be used to check whether an action was created by [`createAsyncThunk`](./createAsyncThunk).
4949

5050
```ts title="isAsyncThunkAction usage"
51-
import { isAsyncThunkAction, AnyAction } from '@reduxjs/toolkit'
51+
import { isAsyncThunkAction } from '@reduxjs/toolkit'
52+
import type { AnyAction } from '@reduxjs/toolkit'
5253
import { requestThunk1, requestThunk2 } from '@virtual/matchers'
5354

5455
const isARequestAction = isAsyncThunkAction(requestThunk1, requestThunk2)
@@ -65,7 +66,8 @@ function handleRequestAction(action: AnyAction) {
6566
A higher-order function that returns a type guard function that may be used to check whether an action is a 'pending' action creator from the `createAsyncThunk` promise lifecycle.
6667

6768
```ts title="isPending usage"
68-
import { isPending, AnyAction } from '@reduxjs/toolkit'
69+
import { isPending } from '@reduxjs/toolkit'
70+
import type { AnyAction } from '@reduxjs/toolkit'
6971
import { requestThunk1, requestThunk2 } from '@virtual/matchers'
7072

7173
const isAPendingAction = isPending(requestThunk1, requestThunk2)
@@ -82,7 +84,8 @@ function handlePendingAction(action: AnyAction) {
8284
A higher-order function that returns a type guard function that may be used to check whether an action is a 'fulfilled'' action creator from the `createAsyncThunk` promise lifecycle.
8385

8486
```ts title="isFulfilled usage"
85-
import { isFulfilled, AnyAction } from '@reduxjs/toolkit'
87+
import { isFulfilled } from '@reduxjs/toolkit'
88+
import type { AnyAction } from '@reduxjs/toolkit'
8689
import { requestThunk1, requestThunk2 } from '@virtual/matchers'
8790

8891
const isAFulfilledAction = isFulfilled(requestThunk1, requestThunk2)
@@ -99,7 +102,8 @@ function handleFulfilledAction(action: AnyAction) {
99102
A higher-order function that returns a type guard function that may be used to check whether an action is a 'rejected' action creator from the `createAsyncThunk` promise lifecycle.
100103

101104
```ts title="isRejected usage"
102-
import { isRejected, AnyAction } from '@reduxjs/toolkit'
105+
import { isRejected } from '@reduxjs/toolkit'
106+
import type { AnyAction } from '@reduxjs/toolkit'
103107
import { requestThunk1, requestThunk2 } from '@virtual/matchers'
104108

105109
const isARejectedAction = isRejected(requestThunk1, requestThunk2)
@@ -116,7 +120,8 @@ function handleRejectedAction(action: AnyAction) {
116120
A higher-order function that returns a type guard function that may be used to check whether an action is a 'rejected' action creator from the `createAsyncThunk` promise lifecycle that was created by [`rejectWithValue`](./createAsyncThunk#handling-thunk-errors).
117121

118122
```ts title="isRejectedWithValue usage"
119-
import { isRejectedWithValue, AnyAction } from '@reduxjs/toolkit'
123+
import { isRejectedWithValue } from '@reduxjs/toolkit'
124+
import type { AnyAction } from '@reduxjs/toolkit'
120125
import { requestThunk1, requestThunk2 } from '@virtual/matchers'
121126

122127
const isARejectedWithValueAction = isRejectedWithValue(
@@ -143,8 +148,8 @@ First, let's examine an unnecessarily complex example:
143148
import {
144149
createAsyncThunk,
145150
createReducer,
146-
PayloadAction,
147151
} from '@reduxjs/toolkit'
152+
import type { PayloadAction } from '@reduxjs/toolkit'
148153

149154
interface Data {
150155
isInteresting: boolean
@@ -213,8 +218,8 @@ import {
213218
initialState,
214219
isSpecial,
215220
isInteresting,
216-
Data,
217221
} from '@virtual/matchers' // This is a fake pkg that provides the types shown above
222+
import type { Data } from '@virtual/matchers' // This is a fake pkg that provides the types shown above
218223
219224
const loadingReducer = createReducer(initialState, (builder) => {
220225
builder
@@ -238,8 +243,10 @@ const loadingReducer = createReducer(initialState, (builder) => {
238243
The function returned by `isAllOf` and `isAnyOf` can also be used as a TypeScript type guard in other contexts.
239244

240245
```ts title="Using isAllOf as a type guard"
241-
import { isAllOf, PayloadAction } from '@reduxjs/toolkit'
242-
import { Data, isSpecial, isInteresting } from '@virtual/matchers' // This is a fake pkg that provides the types shown above
246+
import { isAllOf } from '@reduxjs/toolkit'
247+
import type { PayloadAction } from '@reduxjs/toolkit'
248+
import { isSpecial, isInteresting } from '@virtual/matchers' // This is a fake pkg that provides the types shown above
249+
import type { Data } from '@virtual/matchers' // This is a fake pkg that provides the types shown above
243250
244251
const isSpecialAndInteresting = isAllOf(isSpecial, isInteresting)
245252

@@ -252,7 +259,8 @@ function someFunction(action: PayloadAction<Data>) {
252259
```
253260

254261
```ts title="Using isAnyOf as a type guard"
255-
import { isAnyOf, PayloadAction } from '@reduxjs/toolkit'
262+
import { isAnyOf } from '@reduxjs/toolkit'
263+
import type { PayloadAction } from '@reduxjs/toolkit'
256264
import { Data, isSpecial, isInteresting } from '@virtual/matchers' // this is a fake pkg that provides the types shown above
257265
258266
const isSpecialOrInteresting = isAnyOf(isSpecial, isInteresting)

docs/package.json

-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
"@manaflair/redux-batch": "^1.0.0",
55
"@types/nanoid": "^2.1.0",
66
"@types/react": "^16.9.46",
7-
"@types/react-redux": "^7.1.9",
87
"@types/redux-logger": "^3.0.8",
98
"async-mutex": "^0.3.2",
109
"axios": "^0.20.0",

0 commit comments

Comments
 (0)