@depup/reduxjs__toolkit 2.12.0-depup.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +33 -0
- package/changes.json +18 -0
- package/dist/cjs/index.js +6 -0
- package/dist/cjs/redux-toolkit.development.cjs +2406 -0
- package/dist/cjs/redux-toolkit.development.cjs.map +1 -0
- package/dist/cjs/redux-toolkit.production.min.cjs +3 -0
- package/dist/cjs/redux-toolkit.production.min.cjs.map +1 -0
- package/dist/index.d.mts +2665 -0
- package/dist/index.d.ts +2665 -0
- package/dist/query/cjs/index.js +6 -0
- package/dist/query/cjs/rtk-query.development.cjs +3092 -0
- package/dist/query/cjs/rtk-query.development.cjs.map +1 -0
- package/dist/query/cjs/rtk-query.production.min.cjs +2 -0
- package/dist/query/cjs/rtk-query.production.min.cjs.map +1 -0
- package/dist/query/index.d.mts +3066 -0
- package/dist/query/index.d.ts +3066 -0
- package/dist/query/react/cjs/index.js +6 -0
- package/dist/query/react/cjs/rtk-query-react.development.cjs +748 -0
- package/dist/query/react/cjs/rtk-query-react.development.cjs.map +1 -0
- package/dist/query/react/cjs/rtk-query-react.production.min.cjs +2 -0
- package/dist/query/react/cjs/rtk-query-react.production.min.cjs.map +1 -0
- package/dist/query/react/index.d.mts +1009 -0
- package/dist/query/react/index.d.ts +1009 -0
- package/dist/query/react/rtk-query-react.browser.mjs +2 -0
- package/dist/query/react/rtk-query-react.browser.mjs.map +1 -0
- package/dist/query/react/rtk-query-react.legacy-esm.js +740 -0
- package/dist/query/react/rtk-query-react.legacy-esm.js.map +1 -0
- package/dist/query/react/rtk-query-react.modern.mjs +705 -0
- package/dist/query/react/rtk-query-react.modern.mjs.map +1 -0
- package/dist/query/rtk-query.browser.mjs +2 -0
- package/dist/query/rtk-query.browser.mjs.map +1 -0
- package/dist/query/rtk-query.legacy-esm.js +3117 -0
- package/dist/query/rtk-query.legacy-esm.js.map +1 -0
- package/dist/query/rtk-query.modern.mjs +3052 -0
- package/dist/query/rtk-query.modern.mjs.map +1 -0
- package/dist/react/cjs/index.js +6 -0
- package/dist/react/cjs/redux-toolkit-react.development.cjs +55 -0
- package/dist/react/cjs/redux-toolkit-react.development.cjs.map +1 -0
- package/dist/react/cjs/redux-toolkit-react.production.min.cjs +2 -0
- package/dist/react/cjs/redux-toolkit-react.production.min.cjs.map +1 -0
- package/dist/react/index.d.mts +22 -0
- package/dist/react/index.d.ts +22 -0
- package/dist/react/redux-toolkit-react.browser.mjs +2 -0
- package/dist/react/redux-toolkit-react.browser.mjs.map +1 -0
- package/dist/react/redux-toolkit-react.legacy-esm.js +47 -0
- package/dist/react/redux-toolkit-react.legacy-esm.js.map +1 -0
- package/dist/react/redux-toolkit-react.modern.mjs +28 -0
- package/dist/react/redux-toolkit-react.modern.mjs.map +1 -0
- package/dist/redux-toolkit.browser.mjs +3 -0
- package/dist/redux-toolkit.browser.mjs.map +1 -0
- package/dist/redux-toolkit.legacy-esm.js +2351 -0
- package/dist/redux-toolkit.legacy-esm.js.map +1 -0
- package/dist/redux-toolkit.modern.mjs +2330 -0
- package/dist/redux-toolkit.modern.mjs.map +1 -0
- package/dist/uncheckedindexed.ts +16 -0
- package/package.json +310 -0
- package/query/package.json +13 -0
- package/query/react/package.json +13 -0
- package/react/package.json +13 -0
- package/skills/build-modern-redux-apps/modern-redux/SKILL.md +304 -0
- package/skills/build-modern-redux-apps/modern-redux/references/store-lifetime.md +53 -0
- package/skills/build-modern-redux-apps/redux-dataflow/SKILL.md +264 -0
- package/skills/evolve-and-diagnose-redux-apps/debug-redux-toolkit-apps/SKILL.md +269 -0
- package/skills/evolve-and-diagnose-redux-apps/migrate-to-modern-redux/SKILL.md +226 -0
- package/skills/manage-server-data/adopt-rtk-query/SKILL.md +385 -0
- package/skills/manage-server-data/adopt-rtk-query/references/endpoint-lifecycle.md +36 -0
- package/skills/model-redux-state/build-slices-and-selectors/SKILL.md +364 -0
- package/skills/model-redux-state/build-slices-and-selectors/references/slice-patterns.md +59 -0
- package/skills/model-redux-state/design-state-ownership/SKILL.md +322 -0
- package/skills/model-redux-state/design-state-ownership/references/state-ownership.md +28 -0
- package/skills/orchestrate-side-effects/handle-side-effects/SKILL.md +271 -0
- package/skills/orchestrate-side-effects/handle-side-effects/references/listener-workflows.md +34 -0
- package/src/actionCreatorInvariantMiddleware.ts +34 -0
- package/src/autoBatchEnhancer.ts +146 -0
- package/src/combineSlices.ts +487 -0
- package/src/configureStore.ts +248 -0
- package/src/createAction.ts +324 -0
- package/src/createAsyncThunk.ts +791 -0
- package/src/createDraftSafeSelector.ts +30 -0
- package/src/createReducer.ts +217 -0
- package/src/createSlice.ts +1079 -0
- package/src/devtoolsExtension.ts +241 -0
- package/src/dynamicMiddleware/index.ts +93 -0
- package/src/dynamicMiddleware/react/index.ts +101 -0
- package/src/dynamicMiddleware/types.ts +82 -0
- package/src/entities/create_adapter.ts +47 -0
- package/src/entities/entity_state.ts +38 -0
- package/src/entities/index.ts +8 -0
- package/src/entities/models.ts +198 -0
- package/src/entities/sorted_state_adapter.ts +266 -0
- package/src/entities/state_adapter.ts +58 -0
- package/src/entities/state_selectors.ts +73 -0
- package/src/entities/unsorted_state_adapter.ts +204 -0
- package/src/entities/utils.ts +68 -0
- package/src/formatProdErrorMessage.ts +13 -0
- package/src/getDefaultEnhancers.ts +31 -0
- package/src/getDefaultMiddleware.ts +113 -0
- package/src/immerImports.ts +7 -0
- package/src/immutableStateInvariantMiddleware.ts +274 -0
- package/src/index.ts +213 -0
- package/src/listenerMiddleware/exceptions.ts +20 -0
- package/src/listenerMiddleware/index.ts +562 -0
- package/src/listenerMiddleware/task.ts +100 -0
- package/src/listenerMiddleware/types.ts +886 -0
- package/src/listenerMiddleware/utils.ts +30 -0
- package/src/mapBuilders.ts +298 -0
- package/src/matchers.ts +365 -0
- package/src/nanoid.ts +20 -0
- package/src/query/HandledError.ts +6 -0
- package/src/query/apiTypes.ts +120 -0
- package/src/query/baseQueryTypes.ts +101 -0
- package/src/query/core/apiState.ts +377 -0
- package/src/query/core/buildInitiate.ts +596 -0
- package/src/query/core/buildMiddleware/batchActions.ts +207 -0
- package/src/query/core/buildMiddleware/cacheCollection.ts +204 -0
- package/src/query/core/buildMiddleware/cacheLifecycle.ts +379 -0
- package/src/query/core/buildMiddleware/devMiddleware.ts +34 -0
- package/src/query/core/buildMiddleware/index.ts +162 -0
- package/src/query/core/buildMiddleware/invalidationByTags.ts +139 -0
- package/src/query/core/buildMiddleware/polling.ts +187 -0
- package/src/query/core/buildMiddleware/queryLifecycle.ts +508 -0
- package/src/query/core/buildMiddleware/types.ts +173 -0
- package/src/query/core/buildMiddleware/windowEventHandling.ts +64 -0
- package/src/query/core/buildSelectors.ts +413 -0
- package/src/query/core/buildSlice.ts +735 -0
- package/src/query/core/buildThunks.ts +1124 -0
- package/src/query/core/index.ts +58 -0
- package/src/query/core/module.ts +727 -0
- package/src/query/core/rtkImports.ts +24 -0
- package/src/query/core/setupListeners.ts +118 -0
- package/src/query/createApi.ts +506 -0
- package/src/query/defaultSerializeQueryArgs.ts +52 -0
- package/src/query/endpointDefinitions.ts +1717 -0
- package/src/query/fakeBaseQuery.ts +21 -0
- package/src/query/fetchBaseQuery.ts +385 -0
- package/src/query/index.ts +105 -0
- package/src/query/react/ApiProvider.tsx +69 -0
- package/src/query/react/buildHooks.ts +2297 -0
- package/src/query/react/constants.ts +2 -0
- package/src/query/react/index.ts +45 -0
- package/src/query/react/module.ts +275 -0
- package/src/query/react/namedHooks.ts +59 -0
- package/src/query/react/reactImports.ts +10 -0
- package/src/query/react/reactReduxImports.ts +1 -0
- package/src/query/react/rtkqImports.ts +8 -0
- package/src/query/react/useSerializedStableValue.ts +17 -0
- package/src/query/react/useShallowStableValue.ts +13 -0
- package/src/query/retry.ts +233 -0
- package/src/query/standardSchema.ts +35 -0
- package/src/query/tsHelpers.ts +48 -0
- package/src/query/utils/capitalize.ts +3 -0
- package/src/query/utils/copyWithStructuralSharing.ts +27 -0
- package/src/query/utils/countObjectKeys.ts +14 -0
- package/src/query/utils/filterMap.ts +27 -0
- package/src/query/utils/getCurrent.ts +6 -0
- package/src/query/utils/getOrInsert.ts +41 -0
- package/src/query/utils/immerImports.ts +9 -0
- package/src/query/utils/index.ts +11 -0
- package/src/query/utils/isAbsoluteUrl.ts +9 -0
- package/src/query/utils/isDocumentVisible.ts +12 -0
- package/src/query/utils/isNotNullish.ts +7 -0
- package/src/query/utils/isOnline.ts +12 -0
- package/src/query/utils/isValidUrl.ts +9 -0
- package/src/query/utils/joinUrls.ts +26 -0
- package/src/query/utils/signals.ts +33 -0
- package/src/react/index.ts +7 -0
- package/src/reduxImports.ts +8 -0
- package/src/reselectImports.ts +1 -0
- package/src/serializableStateInvariantMiddleware.ts +285 -0
- package/src/tsHelpers.ts +214 -0
- package/src/uncheckedindexed.ts +16 -0
- package/src/utils.ts +125 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export const assertFunction: (
|
|
2
|
+
func: unknown,
|
|
3
|
+
expected: string,
|
|
4
|
+
) => asserts func is (...args: unknown[]) => unknown = (
|
|
5
|
+
func: unknown,
|
|
6
|
+
expected: string,
|
|
7
|
+
) => {
|
|
8
|
+
if (typeof func !== 'function') {
|
|
9
|
+
throw new TypeError(`${expected} is not a function`)
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const noop = () => {}
|
|
14
|
+
|
|
15
|
+
export const catchRejection = <T>(
|
|
16
|
+
promise: Promise<T>,
|
|
17
|
+
onError = noop,
|
|
18
|
+
): Promise<T> => {
|
|
19
|
+
promise.catch(onError)
|
|
20
|
+
|
|
21
|
+
return promise
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const addAbortSignalListener = (
|
|
25
|
+
abortSignal: AbortSignal,
|
|
26
|
+
callback: (evt: Event) => void,
|
|
27
|
+
) => {
|
|
28
|
+
abortSignal.addEventListener('abort', callback, { once: true })
|
|
29
|
+
return () => abortSignal.removeEventListener('abort', callback)
|
|
30
|
+
}
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
import type { Action } from 'redux'
|
|
2
|
+
import type {
|
|
3
|
+
CaseReducer,
|
|
4
|
+
CaseReducers,
|
|
5
|
+
ActionMatcherDescriptionCollection,
|
|
6
|
+
} from './createReducer'
|
|
7
|
+
import type { TypeGuard } from './tsHelpers'
|
|
8
|
+
import type { AsyncThunk, AsyncThunkConfig } from './createAsyncThunk'
|
|
9
|
+
|
|
10
|
+
export type AsyncThunkReducers<
|
|
11
|
+
State,
|
|
12
|
+
ThunkArg extends any,
|
|
13
|
+
Returned = unknown,
|
|
14
|
+
ThunkApiConfig extends AsyncThunkConfig = {},
|
|
15
|
+
> = {
|
|
16
|
+
pending?: CaseReducer<
|
|
17
|
+
State,
|
|
18
|
+
ReturnType<AsyncThunk<Returned, ThunkArg, ThunkApiConfig>['pending']>
|
|
19
|
+
>
|
|
20
|
+
rejected?: CaseReducer<
|
|
21
|
+
State,
|
|
22
|
+
ReturnType<AsyncThunk<Returned, ThunkArg, ThunkApiConfig>['rejected']>
|
|
23
|
+
>
|
|
24
|
+
fulfilled?: CaseReducer<
|
|
25
|
+
State,
|
|
26
|
+
ReturnType<AsyncThunk<Returned, ThunkArg, ThunkApiConfig>['fulfilled']>
|
|
27
|
+
>
|
|
28
|
+
settled?: CaseReducer<
|
|
29
|
+
State,
|
|
30
|
+
ReturnType<
|
|
31
|
+
AsyncThunk<Returned, ThunkArg, ThunkApiConfig>['rejected' | 'fulfilled']
|
|
32
|
+
>
|
|
33
|
+
>
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type TypedActionCreator<Type extends string> = {
|
|
37
|
+
(...args: any[]): Action<Type>
|
|
38
|
+
type: Type
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* A builder for an action <-> reducer map.
|
|
43
|
+
*
|
|
44
|
+
* @public
|
|
45
|
+
*/
|
|
46
|
+
export interface ActionReducerMapBuilder<State> {
|
|
47
|
+
/**
|
|
48
|
+
* Adds a case reducer to handle a single exact action type.
|
|
49
|
+
* @remarks
|
|
50
|
+
* All calls to `builder.addCase` must come before any calls to `builder.addMatcher` or `builder.addDefaultCase`.
|
|
51
|
+
* @param actionCreator - Either a plain action type string, or an action creator generated by [`createAction`](./createAction) that can be used to determine the action type.
|
|
52
|
+
* @param reducer - The actual case reducer function.
|
|
53
|
+
*/
|
|
54
|
+
addCase<ActionCreator extends TypedActionCreator<string>>(
|
|
55
|
+
actionCreator: ActionCreator,
|
|
56
|
+
reducer: CaseReducer<State, ReturnType<ActionCreator>>,
|
|
57
|
+
): ActionReducerMapBuilder<State>
|
|
58
|
+
/**
|
|
59
|
+
* Adds a case reducer to handle a single exact action type.
|
|
60
|
+
* @remarks
|
|
61
|
+
* All calls to `builder.addCase` must come before any calls to `builder.addAsyncThunk`, `builder.addMatcher` or `builder.addDefaultCase`.
|
|
62
|
+
* @param actionCreator - Either a plain action type string, or an action creator generated by [`createAction`](./createAction) that can be used to determine the action type.
|
|
63
|
+
* @param reducer - The actual case reducer function.
|
|
64
|
+
*/
|
|
65
|
+
addCase<Type extends string, A extends Action<Type>>(
|
|
66
|
+
type: Type,
|
|
67
|
+
reducer: CaseReducer<State, A>,
|
|
68
|
+
): ActionReducerMapBuilder<State>
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Adds case reducers to handle actions based on a `AsyncThunk` action creator.
|
|
72
|
+
* @remarks
|
|
73
|
+
* All calls to `builder.addAsyncThunk` must come before after any calls to `builder.addCase` and before any calls to `builder.addMatcher` or `builder.addDefaultCase`.
|
|
74
|
+
* @param asyncThunk - The async thunk action creator itself.
|
|
75
|
+
* @param reducers - A mapping from each of the `AsyncThunk` action types to the case reducer that should handle those actions.
|
|
76
|
+
* @example
|
|
77
|
+
* ```ts no-transpile
|
|
78
|
+
* import { createAsyncThunk, createReducer } from '@reduxjs/toolkit';
|
|
79
|
+
*
|
|
80
|
+
* const fetchUserById = createAsyncThunk('users/fetchUser', async (id) => {
|
|
81
|
+
* const response = await fetch(`https://reqres.in/api/users/${id}`);
|
|
82
|
+
* return (await response.json()).data;
|
|
83
|
+
* });
|
|
84
|
+
*
|
|
85
|
+
* const reducer = createReducer(initialState, (builder) => {
|
|
86
|
+
* builder.addAsyncThunk(fetchUserById, {
|
|
87
|
+
* pending: (state, action) => {
|
|
88
|
+
* state.fetchUserById.loading = 'pending';
|
|
89
|
+
* },
|
|
90
|
+
* fulfilled: (state, action) => {
|
|
91
|
+
* state.fetchUserById.data = action.payload;
|
|
92
|
+
* },
|
|
93
|
+
* rejected: (state, action) => {
|
|
94
|
+
* state.fetchUserById.error = action.error;
|
|
95
|
+
* },
|
|
96
|
+
* settled: (state, action) => {
|
|
97
|
+
* state.fetchUserById.loading = action.meta.requestStatus;
|
|
98
|
+
* },
|
|
99
|
+
* });
|
|
100
|
+
* });
|
|
101
|
+
* ```
|
|
102
|
+
*/
|
|
103
|
+
addAsyncThunk<
|
|
104
|
+
Returned,
|
|
105
|
+
ThunkArg,
|
|
106
|
+
ThunkApiConfig extends AsyncThunkConfig = {},
|
|
107
|
+
>(
|
|
108
|
+
asyncThunk: AsyncThunk<Returned, ThunkArg, ThunkApiConfig>,
|
|
109
|
+
reducers: AsyncThunkReducers<State, ThunkArg, Returned, ThunkApiConfig>,
|
|
110
|
+
): Omit<ActionReducerMapBuilder<State>, 'addCase'>
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Allows you to match your incoming actions against your own filter function instead of only the `action.type` property.
|
|
114
|
+
* @remarks
|
|
115
|
+
* If multiple matcher reducers match, all of them will be executed in the order
|
|
116
|
+
* they were defined in - even if a case reducer already matched.
|
|
117
|
+
* All calls to `builder.addMatcher` must come after any calls to `builder.addCase` and `builder.addAsyncThunk` and before any calls to `builder.addDefaultCase`.
|
|
118
|
+
* @param matcher - A matcher function. In TypeScript, this should be a [type predicate](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates)
|
|
119
|
+
* function
|
|
120
|
+
* @param reducer - The actual case reducer function.
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* ```ts
|
|
124
|
+
* import type { AsyncThunk, UnknownAction } from '@reduxjs/toolkit';
|
|
125
|
+
* import { createAction, createReducer } from '@reduxjs/toolkit';
|
|
126
|
+
*
|
|
127
|
+
* type GenericAsyncThunk = AsyncThunk<unknown, unknown, any>;
|
|
128
|
+
*
|
|
129
|
+
* type PendingAction = ReturnType<GenericAsyncThunk['pending']>;
|
|
130
|
+
* type RejectedAction = ReturnType<GenericAsyncThunk['rejected']>;
|
|
131
|
+
* type FulfilledAction = ReturnType<GenericAsyncThunk['fulfilled']>;
|
|
132
|
+
*
|
|
133
|
+
* const initialState: Record<string, string> = {};
|
|
134
|
+
* const resetAction = createAction('reset-tracked-loading-state');
|
|
135
|
+
*
|
|
136
|
+
* function isPendingAction(action: UnknownAction): action is PendingAction {
|
|
137
|
+
* return typeof action.type === 'string' && action.type.endsWith('/pending');
|
|
138
|
+
* }
|
|
139
|
+
*
|
|
140
|
+
* const reducer = createReducer(initialState, (builder) => {
|
|
141
|
+
* builder
|
|
142
|
+
* .addCase(resetAction, () => initialState)
|
|
143
|
+
* // matcher can be defined outside as a type predicate function
|
|
144
|
+
* .addMatcher(isPendingAction, (state, action) => {
|
|
145
|
+
* state[action.meta.requestId] = 'pending';
|
|
146
|
+
* })
|
|
147
|
+
* .addMatcher(
|
|
148
|
+
* // matcher can be defined inline as a type predicate function
|
|
149
|
+
* (action): action is RejectedAction => action.type.endsWith('/rejected'),
|
|
150
|
+
* (state, action) => {
|
|
151
|
+
* state[action.meta.requestId] = 'rejected';
|
|
152
|
+
* },
|
|
153
|
+
* )
|
|
154
|
+
* // matcher can just return boolean and the matcher can receive a generic argument
|
|
155
|
+
* .addMatcher<FulfilledAction>(
|
|
156
|
+
* (action) => action.type.endsWith('/fulfilled'),
|
|
157
|
+
* (state, action) => {
|
|
158
|
+
* state[action.meta.requestId] = 'fulfilled';
|
|
159
|
+
* },
|
|
160
|
+
* );
|
|
161
|
+
* });
|
|
162
|
+
* ```
|
|
163
|
+
*/
|
|
164
|
+
addMatcher<A>(
|
|
165
|
+
matcher: TypeGuard<A> | ((action: any) => boolean),
|
|
166
|
+
reducer: CaseReducer<State, A extends Action ? A : A & Action>,
|
|
167
|
+
): Omit<ActionReducerMapBuilder<State>, 'addCase' | 'addAsyncThunk'>
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Adds a "default case" reducer that is executed if no case reducer and no matcher
|
|
171
|
+
* reducer was executed for this action.
|
|
172
|
+
* @param reducer - The fallback "default case" reducer function.
|
|
173
|
+
*
|
|
174
|
+
* @example
|
|
175
|
+
* ```ts
|
|
176
|
+
* import { createReducer } from '@reduxjs/toolkit';
|
|
177
|
+
*
|
|
178
|
+
* const initialState = { otherActions: 0 };
|
|
179
|
+
*
|
|
180
|
+
* const reducer = createReducer(initialState, (builder) => {
|
|
181
|
+
* builder
|
|
182
|
+
* // .addCase(...)
|
|
183
|
+
* // .addMatcher(...)
|
|
184
|
+
* .addDefaultCase((state, action) => {
|
|
185
|
+
* state.otherActions++;
|
|
186
|
+
* });
|
|
187
|
+
* });
|
|
188
|
+
* ```
|
|
189
|
+
*/
|
|
190
|
+
addDefaultCase(reducer: CaseReducer<State, Action>): {}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export function executeReducerBuilderCallback<S>(
|
|
194
|
+
builderCallback: (builder: ActionReducerMapBuilder<S>) => void,
|
|
195
|
+
): [
|
|
196
|
+
CaseReducers<S, any>,
|
|
197
|
+
ActionMatcherDescriptionCollection<S>,
|
|
198
|
+
CaseReducer<S, Action> | undefined,
|
|
199
|
+
] {
|
|
200
|
+
const actionsMap: CaseReducers<S, any> = {}
|
|
201
|
+
const actionMatchers: ActionMatcherDescriptionCollection<S> = []
|
|
202
|
+
let defaultCaseReducer: CaseReducer<S, Action> | undefined
|
|
203
|
+
const builder = {
|
|
204
|
+
addCase(
|
|
205
|
+
typeOrActionCreator: string | TypedActionCreator<any>,
|
|
206
|
+
reducer: CaseReducer<S>,
|
|
207
|
+
) {
|
|
208
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
209
|
+
/*
|
|
210
|
+
to keep the definition by the user in line with actual behavior,
|
|
211
|
+
we enforce `addCase` to always be called before calling `addMatcher`
|
|
212
|
+
as matching cases take precedence over matchers
|
|
213
|
+
*/
|
|
214
|
+
if (actionMatchers.length > 0) {
|
|
215
|
+
throw new Error(
|
|
216
|
+
'`builder.addCase` should only be called before calling `builder.addMatcher`',
|
|
217
|
+
)
|
|
218
|
+
}
|
|
219
|
+
if (defaultCaseReducer) {
|
|
220
|
+
throw new Error(
|
|
221
|
+
'`builder.addCase` should only be called before calling `builder.addDefaultCase`',
|
|
222
|
+
)
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
const type =
|
|
226
|
+
typeof typeOrActionCreator === 'string'
|
|
227
|
+
? typeOrActionCreator
|
|
228
|
+
: typeOrActionCreator.type
|
|
229
|
+
if (!type) {
|
|
230
|
+
throw new Error(
|
|
231
|
+
'`builder.addCase` cannot be called with an empty action type',
|
|
232
|
+
)
|
|
233
|
+
}
|
|
234
|
+
if (type in actionsMap) {
|
|
235
|
+
throw new Error(
|
|
236
|
+
'`builder.addCase` cannot be called with two reducers for the same action type ' +
|
|
237
|
+
`'${type}'`,
|
|
238
|
+
)
|
|
239
|
+
}
|
|
240
|
+
actionsMap[type] = reducer
|
|
241
|
+
return builder
|
|
242
|
+
},
|
|
243
|
+
addAsyncThunk<
|
|
244
|
+
Returned,
|
|
245
|
+
ThunkArg,
|
|
246
|
+
ThunkApiConfig extends AsyncThunkConfig = {},
|
|
247
|
+
>(
|
|
248
|
+
asyncThunk: AsyncThunk<Returned, ThunkArg, ThunkApiConfig>,
|
|
249
|
+
reducers: AsyncThunkReducers<S, ThunkArg, Returned, ThunkApiConfig>,
|
|
250
|
+
) {
|
|
251
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
252
|
+
// since this uses both action cases and matchers, we can't enforce the order in runtime other than checking for default case
|
|
253
|
+
if (defaultCaseReducer) {
|
|
254
|
+
throw new Error(
|
|
255
|
+
'`builder.addAsyncThunk` should only be called before calling `builder.addDefaultCase`',
|
|
256
|
+
)
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
if (reducers.pending)
|
|
260
|
+
actionsMap[asyncThunk.pending.type] = reducers.pending
|
|
261
|
+
if (reducers.rejected)
|
|
262
|
+
actionsMap[asyncThunk.rejected.type] = reducers.rejected
|
|
263
|
+
if (reducers.fulfilled)
|
|
264
|
+
actionsMap[asyncThunk.fulfilled.type] = reducers.fulfilled
|
|
265
|
+
if (reducers.settled)
|
|
266
|
+
actionMatchers.push({
|
|
267
|
+
matcher: asyncThunk.settled,
|
|
268
|
+
reducer: reducers.settled,
|
|
269
|
+
})
|
|
270
|
+
return builder
|
|
271
|
+
},
|
|
272
|
+
addMatcher<A>(
|
|
273
|
+
matcher: TypeGuard<A>,
|
|
274
|
+
reducer: CaseReducer<S, A extends Action ? A : A & Action>,
|
|
275
|
+
) {
|
|
276
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
277
|
+
if (defaultCaseReducer) {
|
|
278
|
+
throw new Error(
|
|
279
|
+
'`builder.addMatcher` should only be called before calling `builder.addDefaultCase`',
|
|
280
|
+
)
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
actionMatchers.push({ matcher, reducer })
|
|
284
|
+
return builder
|
|
285
|
+
},
|
|
286
|
+
addDefaultCase(reducer: CaseReducer<S, Action>) {
|
|
287
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
288
|
+
if (defaultCaseReducer) {
|
|
289
|
+
throw new Error('`builder.addDefaultCase` can only be called once')
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
defaultCaseReducer = reducer
|
|
293
|
+
return builder
|
|
294
|
+
},
|
|
295
|
+
}
|
|
296
|
+
builderCallback(builder)
|
|
297
|
+
return [actionsMap, actionMatchers, defaultCaseReducer]
|
|
298
|
+
}
|
package/src/matchers.ts
ADDED
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ActionFromMatcher,
|
|
3
|
+
Matcher,
|
|
4
|
+
UnionToIntersection,
|
|
5
|
+
} from './tsHelpers'
|
|
6
|
+
import { hasMatchFunction } from './tsHelpers'
|
|
7
|
+
import type {
|
|
8
|
+
AsyncThunk,
|
|
9
|
+
AsyncThunkFulfilledActionCreator,
|
|
10
|
+
AsyncThunkPendingActionCreator,
|
|
11
|
+
AsyncThunkRejectedActionCreator,
|
|
12
|
+
} from './createAsyncThunk'
|
|
13
|
+
|
|
14
|
+
/** @public */
|
|
15
|
+
export type ActionMatchingAnyOf<Matchers extends Matcher<any>[]> =
|
|
16
|
+
ActionFromMatcher<Matchers[number]>
|
|
17
|
+
|
|
18
|
+
/** @public */
|
|
19
|
+
export type ActionMatchingAllOf<Matchers extends Matcher<any>[]> =
|
|
20
|
+
UnionToIntersection<ActionMatchingAnyOf<Matchers>>
|
|
21
|
+
|
|
22
|
+
const matches = (matcher: Matcher<any>, action: any) => {
|
|
23
|
+
if (hasMatchFunction(matcher)) {
|
|
24
|
+
return matcher.match(action)
|
|
25
|
+
} else {
|
|
26
|
+
return matcher(action)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* A higher-order function that returns a function that may be used to check
|
|
32
|
+
* whether an action matches any one of the supplied type guards or action
|
|
33
|
+
* creators.
|
|
34
|
+
*
|
|
35
|
+
* @param matchers The type guards or action creators to match against.
|
|
36
|
+
*
|
|
37
|
+
* @public
|
|
38
|
+
*/
|
|
39
|
+
export function isAnyOf<Matchers extends Matcher<any>[]>(
|
|
40
|
+
...matchers: Matchers
|
|
41
|
+
) {
|
|
42
|
+
return (action: any): action is ActionMatchingAnyOf<Matchers> => {
|
|
43
|
+
return matchers.some((matcher) => matches(matcher, action))
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* A higher-order function that returns a function that may be used to check
|
|
49
|
+
* whether an action matches all of the supplied type guards or action
|
|
50
|
+
* creators.
|
|
51
|
+
*
|
|
52
|
+
* @param matchers The type guards or action creators to match against.
|
|
53
|
+
*
|
|
54
|
+
* @public
|
|
55
|
+
*/
|
|
56
|
+
export function isAllOf<Matchers extends Matcher<any>[]>(
|
|
57
|
+
...matchers: Matchers
|
|
58
|
+
) {
|
|
59
|
+
return (action: any): action is ActionMatchingAllOf<Matchers> => {
|
|
60
|
+
return matchers.every((matcher) => matches(matcher, action))
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* @param action A redux action
|
|
66
|
+
* @param validStatus An array of valid meta.requestStatus values
|
|
67
|
+
*
|
|
68
|
+
* @internal
|
|
69
|
+
*/
|
|
70
|
+
export function hasExpectedRequestMetadata(
|
|
71
|
+
action: any,
|
|
72
|
+
validStatus: readonly string[],
|
|
73
|
+
) {
|
|
74
|
+
if (!action || !action.meta) return false
|
|
75
|
+
|
|
76
|
+
const hasValidRequestId = typeof action.meta.requestId === 'string'
|
|
77
|
+
const hasValidRequestStatus =
|
|
78
|
+
validStatus.indexOf(action.meta.requestStatus) > -1
|
|
79
|
+
|
|
80
|
+
return hasValidRequestId && hasValidRequestStatus
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function isAsyncThunkArray(a: [any] | AnyAsyncThunk[]): a is AnyAsyncThunk[] {
|
|
84
|
+
return (
|
|
85
|
+
typeof a[0] === 'function' &&
|
|
86
|
+
'pending' in a[0] &&
|
|
87
|
+
'fulfilled' in a[0] &&
|
|
88
|
+
'rejected' in a[0]
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export type UnknownAsyncThunkPendingAction = ReturnType<
|
|
93
|
+
AsyncThunkPendingActionCreator<unknown>
|
|
94
|
+
>
|
|
95
|
+
|
|
96
|
+
export type PendingActionFromAsyncThunk<T extends AnyAsyncThunk> =
|
|
97
|
+
ActionFromMatcher<T['pending']>
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* A higher-order function that returns a function that may be used to check
|
|
101
|
+
* whether an action was created by an async thunk action creator, and that
|
|
102
|
+
* the action is pending.
|
|
103
|
+
*
|
|
104
|
+
* @public
|
|
105
|
+
*/
|
|
106
|
+
export function isPending(): (
|
|
107
|
+
action: any,
|
|
108
|
+
) => action is UnknownAsyncThunkPendingAction
|
|
109
|
+
/**
|
|
110
|
+
* A higher-order function that returns a function that may be used to check
|
|
111
|
+
* whether an action belongs to one of the provided async thunk action creators,
|
|
112
|
+
* and that the action is pending.
|
|
113
|
+
*
|
|
114
|
+
* @param asyncThunks (optional) The async thunk action creators to match against.
|
|
115
|
+
*
|
|
116
|
+
* @public
|
|
117
|
+
*/
|
|
118
|
+
export function isPending<
|
|
119
|
+
AsyncThunks extends [AnyAsyncThunk, ...AnyAsyncThunk[]],
|
|
120
|
+
>(
|
|
121
|
+
...asyncThunks: AsyncThunks
|
|
122
|
+
): (action: any) => action is PendingActionFromAsyncThunk<AsyncThunks[number]>
|
|
123
|
+
/**
|
|
124
|
+
* Tests if `action` is a pending thunk action
|
|
125
|
+
* @public
|
|
126
|
+
*/
|
|
127
|
+
export function isPending(action: any): action is UnknownAsyncThunkPendingAction
|
|
128
|
+
export function isPending<
|
|
129
|
+
AsyncThunks extends [AnyAsyncThunk, ...AnyAsyncThunk[]],
|
|
130
|
+
>(...asyncThunks: AsyncThunks | [any]) {
|
|
131
|
+
if (asyncThunks.length === 0) {
|
|
132
|
+
return (action: any) => hasExpectedRequestMetadata(action, ['pending'])
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (!isAsyncThunkArray(asyncThunks)) {
|
|
136
|
+
return isPending()(asyncThunks[0])
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return isAnyOf(...asyncThunks.map((asyncThunk) => asyncThunk.pending))
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export type UnknownAsyncThunkRejectedAction = ReturnType<
|
|
143
|
+
AsyncThunkRejectedActionCreator<unknown, unknown>
|
|
144
|
+
>
|
|
145
|
+
|
|
146
|
+
export type RejectedActionFromAsyncThunk<T extends AnyAsyncThunk> =
|
|
147
|
+
ActionFromMatcher<T['rejected']>
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* A higher-order function that returns a function that may be used to check
|
|
151
|
+
* whether an action was created by an async thunk action creator, and that
|
|
152
|
+
* the action is rejected.
|
|
153
|
+
*
|
|
154
|
+
* @public
|
|
155
|
+
*/
|
|
156
|
+
export function isRejected(): (
|
|
157
|
+
action: any,
|
|
158
|
+
) => action is UnknownAsyncThunkRejectedAction
|
|
159
|
+
/**
|
|
160
|
+
* A higher-order function that returns a function that may be used to check
|
|
161
|
+
* whether an action belongs to one of the provided async thunk action creators,
|
|
162
|
+
* and that the action is rejected.
|
|
163
|
+
*
|
|
164
|
+
* @param asyncThunks (optional) The async thunk action creators to match against.
|
|
165
|
+
*
|
|
166
|
+
* @public
|
|
167
|
+
*/
|
|
168
|
+
export function isRejected<
|
|
169
|
+
AsyncThunks extends [AnyAsyncThunk, ...AnyAsyncThunk[]],
|
|
170
|
+
>(
|
|
171
|
+
...asyncThunks: AsyncThunks
|
|
172
|
+
): (action: any) => action is RejectedActionFromAsyncThunk<AsyncThunks[number]>
|
|
173
|
+
/**
|
|
174
|
+
* Tests if `action` is a rejected thunk action
|
|
175
|
+
* @public
|
|
176
|
+
*/
|
|
177
|
+
export function isRejected(
|
|
178
|
+
action: any,
|
|
179
|
+
): action is UnknownAsyncThunkRejectedAction
|
|
180
|
+
export function isRejected<
|
|
181
|
+
AsyncThunks extends [AnyAsyncThunk, ...AnyAsyncThunk[]],
|
|
182
|
+
>(...asyncThunks: AsyncThunks | [any]) {
|
|
183
|
+
if (asyncThunks.length === 0) {
|
|
184
|
+
return (action: any) => hasExpectedRequestMetadata(action, ['rejected'])
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (!isAsyncThunkArray(asyncThunks)) {
|
|
188
|
+
return isRejected()(asyncThunks[0])
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return isAnyOf(...asyncThunks.map((asyncThunk) => asyncThunk.rejected))
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export type UnknownAsyncThunkRejectedWithValueAction = ReturnType<
|
|
195
|
+
AsyncThunkRejectedActionCreator<unknown, unknown>
|
|
196
|
+
>
|
|
197
|
+
|
|
198
|
+
export type RejectedWithValueActionFromAsyncThunk<T extends AnyAsyncThunk> =
|
|
199
|
+
ActionFromMatcher<T['rejected']> &
|
|
200
|
+
(T extends AsyncThunk<any, any, { rejectValue: infer RejectedValue }>
|
|
201
|
+
? { payload: RejectedValue }
|
|
202
|
+
: unknown)
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* A higher-order function that returns a function that may be used to check
|
|
206
|
+
* whether an action was created by an async thunk action creator, and that
|
|
207
|
+
* the action is rejected with value.
|
|
208
|
+
*
|
|
209
|
+
* @public
|
|
210
|
+
*/
|
|
211
|
+
export function isRejectedWithValue(): (
|
|
212
|
+
action: any,
|
|
213
|
+
) => action is UnknownAsyncThunkRejectedAction
|
|
214
|
+
/**
|
|
215
|
+
* A higher-order function that returns a function that may be used to check
|
|
216
|
+
* whether an action belongs to one of the provided async thunk action creators,
|
|
217
|
+
* and that the action is rejected with value.
|
|
218
|
+
*
|
|
219
|
+
* @param asyncThunks (optional) The async thunk action creators to match against.
|
|
220
|
+
*
|
|
221
|
+
* @public
|
|
222
|
+
*/
|
|
223
|
+
export function isRejectedWithValue<
|
|
224
|
+
AsyncThunks extends [AnyAsyncThunk, ...AnyAsyncThunk[]],
|
|
225
|
+
>(
|
|
226
|
+
...asyncThunks: AsyncThunks
|
|
227
|
+
): (
|
|
228
|
+
action: any,
|
|
229
|
+
) => action is RejectedWithValueActionFromAsyncThunk<AsyncThunks[number]>
|
|
230
|
+
/**
|
|
231
|
+
* Tests if `action` is a rejected thunk action with value
|
|
232
|
+
* @public
|
|
233
|
+
*/
|
|
234
|
+
export function isRejectedWithValue(
|
|
235
|
+
action: any,
|
|
236
|
+
): action is UnknownAsyncThunkRejectedAction
|
|
237
|
+
export function isRejectedWithValue<
|
|
238
|
+
AsyncThunks extends [AnyAsyncThunk, ...AnyAsyncThunk[]],
|
|
239
|
+
>(...asyncThunks: AsyncThunks | [any]) {
|
|
240
|
+
const hasFlag = (action: any): action is any => {
|
|
241
|
+
return action && action.meta && action.meta.rejectedWithValue
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (asyncThunks.length === 0) {
|
|
245
|
+
return isAllOf(isRejected(...asyncThunks), hasFlag)
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (!isAsyncThunkArray(asyncThunks)) {
|
|
249
|
+
return isRejectedWithValue()(asyncThunks[0])
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return isAllOf(isRejected(...asyncThunks), hasFlag)
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export type UnknownAsyncThunkFulfilledAction = ReturnType<
|
|
256
|
+
AsyncThunkFulfilledActionCreator<unknown, unknown>
|
|
257
|
+
>
|
|
258
|
+
|
|
259
|
+
export type FulfilledActionFromAsyncThunk<T extends AnyAsyncThunk> =
|
|
260
|
+
ActionFromMatcher<T['fulfilled']>
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* A higher-order function that returns a function that may be used to check
|
|
264
|
+
* whether an action was created by an async thunk action creator, and that
|
|
265
|
+
* the action is fulfilled.
|
|
266
|
+
*
|
|
267
|
+
* @public
|
|
268
|
+
*/
|
|
269
|
+
export function isFulfilled(): (
|
|
270
|
+
action: any,
|
|
271
|
+
) => action is UnknownAsyncThunkFulfilledAction
|
|
272
|
+
/**
|
|
273
|
+
* A higher-order function that returns a function that may be used to check
|
|
274
|
+
* whether an action belongs to one of the provided async thunk action creators,
|
|
275
|
+
* and that the action is fulfilled.
|
|
276
|
+
*
|
|
277
|
+
* @param asyncThunks (optional) The async thunk action creators to match against.
|
|
278
|
+
*
|
|
279
|
+
* @public
|
|
280
|
+
*/
|
|
281
|
+
export function isFulfilled<
|
|
282
|
+
AsyncThunks extends [AnyAsyncThunk, ...AnyAsyncThunk[]],
|
|
283
|
+
>(
|
|
284
|
+
...asyncThunks: AsyncThunks
|
|
285
|
+
): (action: any) => action is FulfilledActionFromAsyncThunk<AsyncThunks[number]>
|
|
286
|
+
/**
|
|
287
|
+
* Tests if `action` is a fulfilled thunk action
|
|
288
|
+
* @public
|
|
289
|
+
*/
|
|
290
|
+
export function isFulfilled(
|
|
291
|
+
action: any,
|
|
292
|
+
): action is UnknownAsyncThunkFulfilledAction
|
|
293
|
+
export function isFulfilled<
|
|
294
|
+
AsyncThunks extends [AnyAsyncThunk, ...AnyAsyncThunk[]],
|
|
295
|
+
>(...asyncThunks: AsyncThunks | [any]) {
|
|
296
|
+
if (asyncThunks.length === 0) {
|
|
297
|
+
return (action: any) => hasExpectedRequestMetadata(action, ['fulfilled'])
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (!isAsyncThunkArray(asyncThunks)) {
|
|
301
|
+
return isFulfilled()(asyncThunks[0])
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
return isAnyOf(...asyncThunks.map((asyncThunk) => asyncThunk.fulfilled))
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
export type UnknownAsyncThunkAction =
|
|
308
|
+
| UnknownAsyncThunkPendingAction
|
|
309
|
+
| UnknownAsyncThunkRejectedAction
|
|
310
|
+
| UnknownAsyncThunkFulfilledAction
|
|
311
|
+
|
|
312
|
+
export type AnyAsyncThunk = {
|
|
313
|
+
pending: { match: (action: any) => action is any }
|
|
314
|
+
fulfilled: { match: (action: any) => action is any }
|
|
315
|
+
rejected: { match: (action: any) => action is any }
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
export type ActionsFromAsyncThunk<T extends AnyAsyncThunk> =
|
|
319
|
+
| ActionFromMatcher<T['pending']>
|
|
320
|
+
| ActionFromMatcher<T['fulfilled']>
|
|
321
|
+
| ActionFromMatcher<T['rejected']>
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* A higher-order function that returns a function that may be used to check
|
|
325
|
+
* whether an action was created by an async thunk action creator.
|
|
326
|
+
*
|
|
327
|
+
* @public
|
|
328
|
+
*/
|
|
329
|
+
export function isAsyncThunkAction(): (
|
|
330
|
+
action: any,
|
|
331
|
+
) => action is UnknownAsyncThunkAction
|
|
332
|
+
/**
|
|
333
|
+
* A higher-order function that returns a function that may be used to check
|
|
334
|
+
* whether an action belongs to one of the provided async thunk action creators.
|
|
335
|
+
*
|
|
336
|
+
* @param asyncThunks (optional) The async thunk action creators to match against.
|
|
337
|
+
*
|
|
338
|
+
* @public
|
|
339
|
+
*/
|
|
340
|
+
export function isAsyncThunkAction<
|
|
341
|
+
AsyncThunks extends [AnyAsyncThunk, ...AnyAsyncThunk[]],
|
|
342
|
+
>(
|
|
343
|
+
...asyncThunks: AsyncThunks
|
|
344
|
+
): (action: any) => action is ActionsFromAsyncThunk<AsyncThunks[number]>
|
|
345
|
+
/**
|
|
346
|
+
* Tests if `action` is a thunk action
|
|
347
|
+
* @public
|
|
348
|
+
*/
|
|
349
|
+
export function isAsyncThunkAction(
|
|
350
|
+
action: any,
|
|
351
|
+
): action is UnknownAsyncThunkAction
|
|
352
|
+
export function isAsyncThunkAction<
|
|
353
|
+
AsyncThunks extends [AnyAsyncThunk, ...AnyAsyncThunk[]],
|
|
354
|
+
>(...asyncThunks: AsyncThunks | [any]) {
|
|
355
|
+
if (asyncThunks.length === 0) {
|
|
356
|
+
return (action: any) =>
|
|
357
|
+
hasExpectedRequestMetadata(action, ['pending', 'fulfilled', 'rejected'])
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
if (!isAsyncThunkArray(asyncThunks)) {
|
|
361
|
+
return isAsyncThunkAction()(asyncThunks[0])
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
return isAnyOf(...asyncThunks.flatMap(asyncThunk => [asyncThunk.pending, asyncThunk.rejected, asyncThunk.fulfilled]))
|
|
365
|
+
}
|