@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,1079 @@
|
|
|
1
|
+
import type { Action, Reducer, UnknownAction } from 'redux'
|
|
2
|
+
import type { Selector } from 'reselect'
|
|
3
|
+
import type { InjectConfig } from './combineSlices'
|
|
4
|
+
import type {
|
|
5
|
+
ActionCreatorWithoutPayload,
|
|
6
|
+
PayloadAction,
|
|
7
|
+
PayloadActionCreator,
|
|
8
|
+
PrepareAction,
|
|
9
|
+
_ActionCreatorWithPreparedPayload,
|
|
10
|
+
} from './createAction'
|
|
11
|
+
import { createAction } from './createAction'
|
|
12
|
+
import type {
|
|
13
|
+
AsyncThunk,
|
|
14
|
+
AsyncThunkConfig,
|
|
15
|
+
AsyncThunkOptions,
|
|
16
|
+
AsyncThunkPayloadCreator,
|
|
17
|
+
OverrideThunkApiConfigs,
|
|
18
|
+
} from './createAsyncThunk'
|
|
19
|
+
import { createAsyncThunk as _createAsyncThunk } from './createAsyncThunk'
|
|
20
|
+
import type {
|
|
21
|
+
ActionMatcherDescriptionCollection,
|
|
22
|
+
CaseReducer,
|
|
23
|
+
ReducerWithInitialState,
|
|
24
|
+
} from './createReducer'
|
|
25
|
+
import { createReducer } from './createReducer'
|
|
26
|
+
import type {
|
|
27
|
+
ActionReducerMapBuilder,
|
|
28
|
+
AsyncThunkReducers,
|
|
29
|
+
TypedActionCreator,
|
|
30
|
+
} from './mapBuilders'
|
|
31
|
+
import { executeReducerBuilderCallback } from './mapBuilders'
|
|
32
|
+
import type { Id, TypeGuard } from './tsHelpers'
|
|
33
|
+
import { getOrInsertComputed } from './utils'
|
|
34
|
+
|
|
35
|
+
const asyncThunkSymbol = /* @__PURE__ */ Symbol.for(
|
|
36
|
+
'rtk-slice-createasyncthunk',
|
|
37
|
+
)
|
|
38
|
+
// type is annotated because it's too long to infer
|
|
39
|
+
export const asyncThunkCreator: {
|
|
40
|
+
[asyncThunkSymbol]: typeof _createAsyncThunk
|
|
41
|
+
} = {
|
|
42
|
+
[asyncThunkSymbol]: _createAsyncThunk,
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
type InjectIntoConfig<NewReducerPath extends string> = InjectConfig & {
|
|
46
|
+
reducerPath?: NewReducerPath
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* The return value of `createSlice`
|
|
51
|
+
*
|
|
52
|
+
* @public
|
|
53
|
+
*/
|
|
54
|
+
export interface Slice<
|
|
55
|
+
State = any,
|
|
56
|
+
CaseReducers extends SliceCaseReducers<State> = SliceCaseReducers<State>,
|
|
57
|
+
Name extends string = string,
|
|
58
|
+
ReducerPath extends string = Name,
|
|
59
|
+
Selectors extends SliceSelectors<State> = SliceSelectors<State>,
|
|
60
|
+
> {
|
|
61
|
+
/**
|
|
62
|
+
* The slice name.
|
|
63
|
+
*/
|
|
64
|
+
name: Name
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* The slice reducer path.
|
|
68
|
+
*/
|
|
69
|
+
reducerPath: ReducerPath
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* The slice's reducer.
|
|
73
|
+
*/
|
|
74
|
+
reducer: Reducer<State>
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Action creators for the types of actions that are handled by the slice
|
|
78
|
+
* reducer.
|
|
79
|
+
*/
|
|
80
|
+
actions: CaseReducerActions<CaseReducers, Name>
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* The individual case reducer functions that were passed in the `reducers` parameter.
|
|
84
|
+
* This enables reuse and testing if they were defined inline when calling `createSlice`.
|
|
85
|
+
*/
|
|
86
|
+
caseReducers: SliceDefinedCaseReducers<CaseReducers>
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Provides access to the initial state value given to the slice.
|
|
90
|
+
* If a lazy state initializer was provided, it will be called and a fresh value returned.
|
|
91
|
+
*/
|
|
92
|
+
getInitialState: () => State
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Get localised slice selectors (expects to be called with *just* the slice's state as the first parameter)
|
|
96
|
+
*/
|
|
97
|
+
getSelectors(): Id<SliceDefinedSelectors<State, Selectors, State>>
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Get globalised slice selectors (`selectState` callback is expected to receive first parameter and return slice state)
|
|
101
|
+
*/
|
|
102
|
+
getSelectors<RootState>(
|
|
103
|
+
selectState: (rootState: RootState) => State,
|
|
104
|
+
): Id<SliceDefinedSelectors<State, Selectors, RootState>>
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Selectors that assume the slice's state is `rootState[slice.reducerPath]` (which is usually the case)
|
|
108
|
+
*
|
|
109
|
+
* Equivalent to `slice.getSelectors((state: RootState) => state[slice.reducerPath])`.
|
|
110
|
+
*/
|
|
111
|
+
get selectors(): Id<
|
|
112
|
+
SliceDefinedSelectors<State, Selectors, { [K in ReducerPath]: State }>
|
|
113
|
+
>
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Inject slice into provided reducer (return value from `combineSlices`), and return injected slice.
|
|
117
|
+
*/
|
|
118
|
+
injectInto<NewReducerPath extends string = ReducerPath>(
|
|
119
|
+
this: this,
|
|
120
|
+
injectable: {
|
|
121
|
+
inject: (
|
|
122
|
+
slice: { reducerPath: string; reducer: Reducer },
|
|
123
|
+
config?: InjectConfig,
|
|
124
|
+
) => void
|
|
125
|
+
},
|
|
126
|
+
config?: InjectIntoConfig<NewReducerPath>,
|
|
127
|
+
): InjectedSlice<State, CaseReducers, Name, NewReducerPath, Selectors>
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Select the slice state, using the slice's current reducerPath.
|
|
131
|
+
*
|
|
132
|
+
* Will throw an error if slice is not found.
|
|
133
|
+
*/
|
|
134
|
+
selectSlice(state: { [K in ReducerPath]: State }): State
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* A slice after being called with `injectInto(reducer)`.
|
|
139
|
+
*
|
|
140
|
+
* Selectors can now be called with an `undefined` value, in which case they use the slice's initial state.
|
|
141
|
+
*/
|
|
142
|
+
type InjectedSlice<
|
|
143
|
+
State = any,
|
|
144
|
+
CaseReducers extends SliceCaseReducers<State> = SliceCaseReducers<State>,
|
|
145
|
+
Name extends string = string,
|
|
146
|
+
ReducerPath extends string = Name,
|
|
147
|
+
Selectors extends SliceSelectors<State> = SliceSelectors<State>,
|
|
148
|
+
> = Omit<
|
|
149
|
+
Slice<State, CaseReducers, Name, ReducerPath, Selectors>,
|
|
150
|
+
'getSelectors' | 'selectors'
|
|
151
|
+
> & {
|
|
152
|
+
/**
|
|
153
|
+
* Get localised slice selectors (expects to be called with *just* the slice's state as the first parameter)
|
|
154
|
+
*/
|
|
155
|
+
getSelectors(): Id<SliceDefinedSelectors<State, Selectors, State | undefined>>
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Get globalised slice selectors (`selectState` callback is expected to receive first parameter and return slice state)
|
|
159
|
+
*/
|
|
160
|
+
getSelectors<RootState>(
|
|
161
|
+
selectState: (rootState: RootState) => State | undefined,
|
|
162
|
+
): Id<SliceDefinedSelectors<State, Selectors, RootState>>
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Selectors that assume the slice's state is `rootState[slice.name]` (which is usually the case)
|
|
166
|
+
*
|
|
167
|
+
* Equivalent to `slice.getSelectors((state: RootState) => state[slice.name])`.
|
|
168
|
+
*/
|
|
169
|
+
get selectors(): Id<
|
|
170
|
+
SliceDefinedSelectors<
|
|
171
|
+
State,
|
|
172
|
+
Selectors,
|
|
173
|
+
{ [K in ReducerPath]?: State | undefined }
|
|
174
|
+
>
|
|
175
|
+
>
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Select the slice state, using the slice's current reducerPath.
|
|
179
|
+
*
|
|
180
|
+
* Returns initial state if slice is not found.
|
|
181
|
+
*/
|
|
182
|
+
selectSlice(state: { [K in ReducerPath]?: State | undefined }): State
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Options for `createSlice()`.
|
|
187
|
+
*
|
|
188
|
+
* @public
|
|
189
|
+
*/
|
|
190
|
+
export interface CreateSliceOptions<
|
|
191
|
+
State = any,
|
|
192
|
+
CR extends SliceCaseReducers<State> = SliceCaseReducers<State>,
|
|
193
|
+
Name extends string = string,
|
|
194
|
+
ReducerPath extends string = Name,
|
|
195
|
+
Selectors extends SliceSelectors<State> = SliceSelectors<State>,
|
|
196
|
+
> {
|
|
197
|
+
/**
|
|
198
|
+
* The slice's name. Used to namespace the generated action types.
|
|
199
|
+
*/
|
|
200
|
+
name: Name
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* The slice's reducer path. Used when injecting into a combined slice reducer.
|
|
204
|
+
*/
|
|
205
|
+
reducerPath?: ReducerPath
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* The initial state that should be used when the reducer is called the first time. This may also be a "lazy initializer" function, which should return an initial state value when called. This will be used whenever the reducer is called with `undefined` as its state value, and is primarily useful for cases like reading initial state from `localStorage`.
|
|
209
|
+
*/
|
|
210
|
+
initialState: State | (() => State)
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* A mapping from action types to action-type-specific *case reducer*
|
|
214
|
+
* functions. For every action type, a matching action creator will be
|
|
215
|
+
* generated using `createAction()`.
|
|
216
|
+
*/
|
|
217
|
+
reducers:
|
|
218
|
+
| ValidateSliceCaseReducers<State, CR>
|
|
219
|
+
| ((creators: ReducerCreators<State>) => CR)
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* A callback that receives a *builder* object to define
|
|
223
|
+
* case reducers via calls to `builder.addCase(actionCreatorOrType, reducer)`.
|
|
224
|
+
*
|
|
225
|
+
*
|
|
226
|
+
* @example
|
|
227
|
+
* ```ts
|
|
228
|
+
* import type { Action } from '@reduxjs/toolkit';
|
|
229
|
+
* import { createAction, createSlice } from '@reduxjs/toolkit';
|
|
230
|
+
*
|
|
231
|
+
* const incrementBy = createAction<number>('incrementBy');
|
|
232
|
+
* const decrement = createAction('decrement');
|
|
233
|
+
*
|
|
234
|
+
* interface RejectedAction extends Action {
|
|
235
|
+
* error: Error;
|
|
236
|
+
* }
|
|
237
|
+
*
|
|
238
|
+
* function isRejectedAction(action: Action): action is RejectedAction {
|
|
239
|
+
* return action.type.endsWith('rejected');
|
|
240
|
+
* }
|
|
241
|
+
*
|
|
242
|
+
* createSlice({
|
|
243
|
+
* name: 'counter',
|
|
244
|
+
* initialState: 0,
|
|
245
|
+
* reducers: {},
|
|
246
|
+
* extraReducers: (builder) => {
|
|
247
|
+
* builder
|
|
248
|
+
* .addCase(incrementBy, (state, action) => {
|
|
249
|
+
* // action is inferred correctly here if using TS
|
|
250
|
+
* })
|
|
251
|
+
* // You can chain calls, or have separate `builder.addCase()` lines each time
|
|
252
|
+
* .addCase(decrement, (state, action) => {})
|
|
253
|
+
* // You can match a range of action types
|
|
254
|
+
* .addMatcher(
|
|
255
|
+
* isRejectedAction,
|
|
256
|
+
* // `action` will be inferred as a RejectedAction due to isRejectedAction being defined as a type guard
|
|
257
|
+
* (state, action) => {},
|
|
258
|
+
* )
|
|
259
|
+
* // and provide a default case if no other handlers matched
|
|
260
|
+
* .addDefaultCase((state, action) => {});
|
|
261
|
+
* },
|
|
262
|
+
* });
|
|
263
|
+
* ```
|
|
264
|
+
*/
|
|
265
|
+
extraReducers?: (builder: ActionReducerMapBuilder<State>) => void
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* A map of selectors that receive the slice's state and any additional arguments, and return a result.
|
|
269
|
+
*/
|
|
270
|
+
selectors?: Selectors
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
export enum ReducerType {
|
|
274
|
+
reducer = 'reducer',
|
|
275
|
+
reducerWithPrepare = 'reducerWithPrepare',
|
|
276
|
+
asyncThunk = 'asyncThunk',
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
type ReducerDefinition<T extends ReducerType = ReducerType> = {
|
|
280
|
+
_reducerDefinitionType: T
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export type CaseReducerDefinition<
|
|
284
|
+
S = any,
|
|
285
|
+
A extends Action = UnknownAction,
|
|
286
|
+
> = CaseReducer<S, A> & ReducerDefinition<ReducerType.reducer>
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* A CaseReducer with a `prepare` method.
|
|
290
|
+
*
|
|
291
|
+
* @public
|
|
292
|
+
*/
|
|
293
|
+
export type CaseReducerWithPrepare<State, Action extends PayloadAction> = {
|
|
294
|
+
reducer: CaseReducer<State, Action>
|
|
295
|
+
prepare: PrepareAction<Action['payload']>
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
export interface CaseReducerWithPrepareDefinition<
|
|
299
|
+
State,
|
|
300
|
+
Action extends PayloadAction,
|
|
301
|
+
> extends CaseReducerWithPrepare<State, Action>,
|
|
302
|
+
ReducerDefinition<ReducerType.reducerWithPrepare> {}
|
|
303
|
+
|
|
304
|
+
type AsyncThunkSliceReducerConfig<
|
|
305
|
+
State,
|
|
306
|
+
ThunkArg extends any,
|
|
307
|
+
Returned = unknown,
|
|
308
|
+
ThunkApiConfig extends AsyncThunkConfig = {},
|
|
309
|
+
> = AsyncThunkReducers<State, ThunkArg, Returned, ThunkApiConfig> & {
|
|
310
|
+
options?: AsyncThunkOptions<ThunkArg, ThunkApiConfig>
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
type AsyncThunkSliceReducerDefinition<
|
|
314
|
+
State,
|
|
315
|
+
ThunkArg extends any,
|
|
316
|
+
Returned = unknown,
|
|
317
|
+
ThunkApiConfig extends AsyncThunkConfig = {},
|
|
318
|
+
> = AsyncThunkSliceReducerConfig<State, ThunkArg, Returned, ThunkApiConfig> &
|
|
319
|
+
ReducerDefinition<ReducerType.asyncThunk> & {
|
|
320
|
+
payloadCreator: AsyncThunkPayloadCreator<Returned, ThunkArg, ThunkApiConfig>
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Providing these as part of the config would cause circular types, so we disallow passing them
|
|
325
|
+
*/
|
|
326
|
+
type PreventCircular<ThunkApiConfig> = {
|
|
327
|
+
[K in keyof ThunkApiConfig]: K extends 'state' | 'dispatch'
|
|
328
|
+
? never
|
|
329
|
+
: ThunkApiConfig[K]
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
interface AsyncThunkCreator<
|
|
333
|
+
State,
|
|
334
|
+
CurriedThunkApiConfig extends
|
|
335
|
+
PreventCircular<AsyncThunkConfig> = PreventCircular<AsyncThunkConfig>,
|
|
336
|
+
> {
|
|
337
|
+
<Returned, ThunkArg = void>(
|
|
338
|
+
payloadCreator: AsyncThunkPayloadCreator<
|
|
339
|
+
Returned,
|
|
340
|
+
ThunkArg,
|
|
341
|
+
CurriedThunkApiConfig
|
|
342
|
+
>,
|
|
343
|
+
config?: AsyncThunkSliceReducerConfig<
|
|
344
|
+
State,
|
|
345
|
+
ThunkArg,
|
|
346
|
+
Returned,
|
|
347
|
+
CurriedThunkApiConfig
|
|
348
|
+
>,
|
|
349
|
+
): AsyncThunkSliceReducerDefinition<
|
|
350
|
+
State,
|
|
351
|
+
ThunkArg,
|
|
352
|
+
Returned,
|
|
353
|
+
CurriedThunkApiConfig
|
|
354
|
+
>
|
|
355
|
+
<
|
|
356
|
+
Returned,
|
|
357
|
+
ThunkArg,
|
|
358
|
+
ThunkApiConfig extends PreventCircular<AsyncThunkConfig> = {},
|
|
359
|
+
>(
|
|
360
|
+
payloadCreator: AsyncThunkPayloadCreator<
|
|
361
|
+
Returned,
|
|
362
|
+
ThunkArg,
|
|
363
|
+
ThunkApiConfig
|
|
364
|
+
>,
|
|
365
|
+
config?: AsyncThunkSliceReducerConfig<
|
|
366
|
+
State,
|
|
367
|
+
ThunkArg,
|
|
368
|
+
Returned,
|
|
369
|
+
ThunkApiConfig
|
|
370
|
+
>,
|
|
371
|
+
): AsyncThunkSliceReducerDefinition<State, ThunkArg, Returned, ThunkApiConfig>
|
|
372
|
+
withTypes<
|
|
373
|
+
ThunkApiConfig extends PreventCircular<AsyncThunkConfig>,
|
|
374
|
+
>(): AsyncThunkCreator<
|
|
375
|
+
State,
|
|
376
|
+
OverrideThunkApiConfigs<CurriedThunkApiConfig, ThunkApiConfig>
|
|
377
|
+
>
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
export interface ReducerCreators<State> {
|
|
381
|
+
reducer(
|
|
382
|
+
caseReducer: CaseReducer<State, PayloadAction>,
|
|
383
|
+
): CaseReducerDefinition<State, PayloadAction>
|
|
384
|
+
reducer<Payload>(
|
|
385
|
+
caseReducer: CaseReducer<State, PayloadAction<Payload>>,
|
|
386
|
+
): CaseReducerDefinition<State, PayloadAction<Payload>>
|
|
387
|
+
|
|
388
|
+
asyncThunk: AsyncThunkCreator<State>
|
|
389
|
+
|
|
390
|
+
preparedReducer<Prepare extends PrepareAction<any>>(
|
|
391
|
+
prepare: Prepare,
|
|
392
|
+
reducer: CaseReducer<
|
|
393
|
+
State,
|
|
394
|
+
ReturnType<_ActionCreatorWithPreparedPayload<Prepare>>
|
|
395
|
+
>,
|
|
396
|
+
): {
|
|
397
|
+
_reducerDefinitionType: ReducerType.reducerWithPrepare
|
|
398
|
+
prepare: Prepare
|
|
399
|
+
reducer: CaseReducer<
|
|
400
|
+
State,
|
|
401
|
+
ReturnType<_ActionCreatorWithPreparedPayload<Prepare>>
|
|
402
|
+
>
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* The type describing a slice's `reducers` option.
|
|
408
|
+
*
|
|
409
|
+
* @public
|
|
410
|
+
*/
|
|
411
|
+
export type SliceCaseReducers<State> =
|
|
412
|
+
| Record<string, ReducerDefinition>
|
|
413
|
+
| Record<
|
|
414
|
+
string,
|
|
415
|
+
| CaseReducer<State, PayloadAction<any>>
|
|
416
|
+
| CaseReducerWithPrepare<State, PayloadAction<any, string, any, any>>
|
|
417
|
+
>
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* The type describing a slice's `selectors` option.
|
|
421
|
+
*/
|
|
422
|
+
export type SliceSelectors<State> = {
|
|
423
|
+
[K: string]: (sliceState: State, ...args: any[]) => any
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
type SliceActionType<
|
|
427
|
+
SliceName extends string,
|
|
428
|
+
ActionName extends keyof any,
|
|
429
|
+
> = ActionName extends string | number ? `${SliceName}/${ActionName}` : string
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Derives the slice's `actions` property from the `reducers` options
|
|
433
|
+
*
|
|
434
|
+
* @public
|
|
435
|
+
*/
|
|
436
|
+
export type CaseReducerActions<
|
|
437
|
+
CaseReducers extends SliceCaseReducers<any>,
|
|
438
|
+
SliceName extends string,
|
|
439
|
+
> = {
|
|
440
|
+
[Type in keyof CaseReducers]: CaseReducers[Type] extends infer Definition
|
|
441
|
+
? Definition extends { prepare: any }
|
|
442
|
+
? ActionCreatorForCaseReducerWithPrepare<
|
|
443
|
+
Definition,
|
|
444
|
+
SliceActionType<SliceName, Type>
|
|
445
|
+
>
|
|
446
|
+
: Definition extends AsyncThunkSliceReducerDefinition<
|
|
447
|
+
any,
|
|
448
|
+
infer ThunkArg,
|
|
449
|
+
infer Returned,
|
|
450
|
+
infer ThunkApiConfig
|
|
451
|
+
>
|
|
452
|
+
? AsyncThunk<Returned, ThunkArg, ThunkApiConfig>
|
|
453
|
+
: Definition extends { reducer: any }
|
|
454
|
+
? ActionCreatorForCaseReducer<
|
|
455
|
+
Definition['reducer'],
|
|
456
|
+
SliceActionType<SliceName, Type>
|
|
457
|
+
>
|
|
458
|
+
: ActionCreatorForCaseReducer<
|
|
459
|
+
Definition,
|
|
460
|
+
SliceActionType<SliceName, Type>
|
|
461
|
+
>
|
|
462
|
+
: never
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Get a `PayloadActionCreator` type for a passed `CaseReducerWithPrepare`
|
|
467
|
+
*
|
|
468
|
+
* @internal
|
|
469
|
+
*/
|
|
470
|
+
type ActionCreatorForCaseReducerWithPrepare<
|
|
471
|
+
CR extends { prepare: any },
|
|
472
|
+
Type extends string,
|
|
473
|
+
> = _ActionCreatorWithPreparedPayload<CR['prepare'], Type>
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Get a `PayloadActionCreator` type for a passed `CaseReducer`
|
|
477
|
+
*
|
|
478
|
+
* @internal
|
|
479
|
+
*/
|
|
480
|
+
type ActionCreatorForCaseReducer<CR, Type extends string> = CR extends (
|
|
481
|
+
state: any,
|
|
482
|
+
action: infer Action,
|
|
483
|
+
) => any
|
|
484
|
+
? Action extends { payload: infer P }
|
|
485
|
+
? PayloadActionCreator<P, Type>
|
|
486
|
+
: ActionCreatorWithoutPayload<Type>
|
|
487
|
+
: ActionCreatorWithoutPayload<Type>
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* Extracts the CaseReducers out of a `reducers` object, even if they are
|
|
491
|
+
* tested into a `CaseReducerWithPrepare`.
|
|
492
|
+
*
|
|
493
|
+
* @internal
|
|
494
|
+
*/
|
|
495
|
+
type SliceDefinedCaseReducers<CaseReducers extends SliceCaseReducers<any>> = {
|
|
496
|
+
[Type in keyof CaseReducers]: CaseReducers[Type] extends infer Definition
|
|
497
|
+
? Definition extends AsyncThunkSliceReducerDefinition<any, any, any>
|
|
498
|
+
? Id<
|
|
499
|
+
Pick<
|
|
500
|
+
Required<Definition>,
|
|
501
|
+
'fulfilled' | 'rejected' | 'pending' | 'settled'
|
|
502
|
+
>
|
|
503
|
+
>
|
|
504
|
+
: Definition extends {
|
|
505
|
+
reducer: infer Reducer
|
|
506
|
+
}
|
|
507
|
+
? Reducer
|
|
508
|
+
: Definition
|
|
509
|
+
: never
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
type RemappedSelector<S extends Selector, NewState> =
|
|
513
|
+
S extends Selector<any, infer R, infer P>
|
|
514
|
+
? Selector<NewState, R, P> & { unwrapped: S }
|
|
515
|
+
: never
|
|
516
|
+
|
|
517
|
+
/**
|
|
518
|
+
* Extracts the final selector type from the `selectors` object.
|
|
519
|
+
*
|
|
520
|
+
* Removes the `string` index signature from the default value.
|
|
521
|
+
*/
|
|
522
|
+
type SliceDefinedSelectors<
|
|
523
|
+
State,
|
|
524
|
+
Selectors extends SliceSelectors<State>,
|
|
525
|
+
RootState,
|
|
526
|
+
> = {
|
|
527
|
+
[K in keyof Selectors as string extends K ? never : K]: RemappedSelector<
|
|
528
|
+
Selectors[K],
|
|
529
|
+
RootState
|
|
530
|
+
>
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
/**
|
|
534
|
+
* Used on a SliceCaseReducers object.
|
|
535
|
+
* Ensures that if a CaseReducer is a `CaseReducerWithPrepare`, that
|
|
536
|
+
* the `reducer` and the `prepare` function use the same type of `payload`.
|
|
537
|
+
*
|
|
538
|
+
* Might do additional such checks in the future.
|
|
539
|
+
*
|
|
540
|
+
* This type is only ever useful if you want to write your own wrapper around
|
|
541
|
+
* `createSlice`. Please don't use it otherwise!
|
|
542
|
+
*
|
|
543
|
+
* @public
|
|
544
|
+
*/
|
|
545
|
+
export type ValidateSliceCaseReducers<
|
|
546
|
+
S,
|
|
547
|
+
ACR extends SliceCaseReducers<S>,
|
|
548
|
+
> = ACR & {
|
|
549
|
+
[T in keyof ACR]: ACR[T] extends {
|
|
550
|
+
reducer(s: S, action?: infer A): any
|
|
551
|
+
}
|
|
552
|
+
? {
|
|
553
|
+
prepare(...a: never[]): Omit<A, 'type'>
|
|
554
|
+
}
|
|
555
|
+
: {}
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
function getType(slice: string, actionKey: string): string {
|
|
559
|
+
return `${slice}/${actionKey}`
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
interface BuildCreateSliceConfig {
|
|
563
|
+
creators?: {
|
|
564
|
+
asyncThunk?: typeof asyncThunkCreator
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
export function buildCreateSlice({ creators }: BuildCreateSliceConfig = {}) {
|
|
569
|
+
const cAT = creators?.asyncThunk?.[asyncThunkSymbol]
|
|
570
|
+
return function createSlice<
|
|
571
|
+
State,
|
|
572
|
+
CaseReducers extends SliceCaseReducers<State>,
|
|
573
|
+
Name extends string,
|
|
574
|
+
Selectors extends SliceSelectors<State>,
|
|
575
|
+
ReducerPath extends string = Name,
|
|
576
|
+
>(
|
|
577
|
+
options: CreateSliceOptions<
|
|
578
|
+
State,
|
|
579
|
+
CaseReducers,
|
|
580
|
+
Name,
|
|
581
|
+
ReducerPath,
|
|
582
|
+
Selectors
|
|
583
|
+
>,
|
|
584
|
+
): Slice<State, CaseReducers, Name, ReducerPath, Selectors> {
|
|
585
|
+
const { name, reducerPath = name as unknown as ReducerPath } = options
|
|
586
|
+
if (!name) {
|
|
587
|
+
throw new Error('`name` is a required option for createSlice')
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
if (
|
|
591
|
+
typeof process !== 'undefined' &&
|
|
592
|
+
process.env.NODE_ENV === 'development'
|
|
593
|
+
) {
|
|
594
|
+
if (options.initialState === undefined) {
|
|
595
|
+
console.error(
|
|
596
|
+
'You must provide an `initialState` value that is not `undefined`. You may have misspelled `initialState`',
|
|
597
|
+
)
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
const reducers =
|
|
602
|
+
(typeof options.reducers === 'function'
|
|
603
|
+
? options.reducers(buildReducerCreators<State>())
|
|
604
|
+
: options.reducers) || {}
|
|
605
|
+
|
|
606
|
+
const reducerNames = Object.keys(reducers)
|
|
607
|
+
|
|
608
|
+
const context: ReducerHandlingContext<State> = {
|
|
609
|
+
sliceCaseReducersByName: {},
|
|
610
|
+
sliceCaseReducersByType: {},
|
|
611
|
+
actionCreators: {},
|
|
612
|
+
sliceMatchers: [],
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
const contextMethods: ReducerHandlingContextMethods<State> = {
|
|
616
|
+
addCase(
|
|
617
|
+
typeOrActionCreator: string | TypedActionCreator<any>,
|
|
618
|
+
reducer: CaseReducer<State>,
|
|
619
|
+
) {
|
|
620
|
+
const type =
|
|
621
|
+
typeof typeOrActionCreator === 'string'
|
|
622
|
+
? typeOrActionCreator
|
|
623
|
+
: typeOrActionCreator.type
|
|
624
|
+
if (!type) {
|
|
625
|
+
throw new Error(
|
|
626
|
+
'`context.addCase` cannot be called with an empty action type',
|
|
627
|
+
)
|
|
628
|
+
}
|
|
629
|
+
if (type in context.sliceCaseReducersByType) {
|
|
630
|
+
throw new Error(
|
|
631
|
+
'`context.addCase` cannot be called with two reducers for the same action type: ' +
|
|
632
|
+
type,
|
|
633
|
+
)
|
|
634
|
+
}
|
|
635
|
+
context.sliceCaseReducersByType[type] = reducer
|
|
636
|
+
return contextMethods
|
|
637
|
+
},
|
|
638
|
+
addMatcher(matcher, reducer) {
|
|
639
|
+
context.sliceMatchers.push({ matcher, reducer })
|
|
640
|
+
return contextMethods
|
|
641
|
+
},
|
|
642
|
+
exposeAction(name, actionCreator) {
|
|
643
|
+
context.actionCreators[name] = actionCreator
|
|
644
|
+
return contextMethods
|
|
645
|
+
},
|
|
646
|
+
exposeCaseReducer(name, reducer) {
|
|
647
|
+
context.sliceCaseReducersByName[name] = reducer
|
|
648
|
+
return contextMethods
|
|
649
|
+
},
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
reducerNames.forEach((reducerName) => {
|
|
653
|
+
const reducerDefinition = reducers[reducerName]
|
|
654
|
+
const reducerDetails: ReducerDetails = {
|
|
655
|
+
reducerName,
|
|
656
|
+
type: getType(name, reducerName),
|
|
657
|
+
createNotation: typeof options.reducers === 'function',
|
|
658
|
+
}
|
|
659
|
+
if (isAsyncThunkSliceReducerDefinition<State>(reducerDefinition)) {
|
|
660
|
+
handleThunkCaseReducerDefinition(
|
|
661
|
+
reducerDetails,
|
|
662
|
+
reducerDefinition,
|
|
663
|
+
contextMethods,
|
|
664
|
+
cAT,
|
|
665
|
+
)
|
|
666
|
+
} else {
|
|
667
|
+
handleNormalReducerDefinition<State>(
|
|
668
|
+
reducerDetails,
|
|
669
|
+
reducerDefinition as any,
|
|
670
|
+
contextMethods,
|
|
671
|
+
)
|
|
672
|
+
}
|
|
673
|
+
})
|
|
674
|
+
|
|
675
|
+
function buildReducer() {
|
|
676
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
677
|
+
if (typeof options.extraReducers === 'object') {
|
|
678
|
+
throw new Error(
|
|
679
|
+
"The object notation for `createSlice.extraReducers` has been removed. Please use the 'builder callback' notation instead: https://redux-toolkit.js.org/api/createSlice",
|
|
680
|
+
)
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
const [
|
|
684
|
+
extraReducers = {},
|
|
685
|
+
actionMatchers = [],
|
|
686
|
+
defaultCaseReducer = undefined,
|
|
687
|
+
] =
|
|
688
|
+
typeof options.extraReducers === 'function'
|
|
689
|
+
? executeReducerBuilderCallback(options.extraReducers)
|
|
690
|
+
: [options.extraReducers]
|
|
691
|
+
|
|
692
|
+
const finalCaseReducers = {
|
|
693
|
+
...extraReducers,
|
|
694
|
+
...context.sliceCaseReducersByType,
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
return createReducer(options.initialState, (builder) => {
|
|
698
|
+
for (let key in finalCaseReducers) {
|
|
699
|
+
builder.addCase(key, finalCaseReducers[key] as CaseReducer<any>)
|
|
700
|
+
}
|
|
701
|
+
for (let sM of context.sliceMatchers) {
|
|
702
|
+
builder.addMatcher(sM.matcher, sM.reducer)
|
|
703
|
+
}
|
|
704
|
+
for (let m of actionMatchers) {
|
|
705
|
+
builder.addMatcher(m.matcher, m.reducer)
|
|
706
|
+
}
|
|
707
|
+
if (defaultCaseReducer) {
|
|
708
|
+
builder.addDefaultCase(defaultCaseReducer)
|
|
709
|
+
}
|
|
710
|
+
})
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
const selectSelf = (state: State) => state
|
|
714
|
+
|
|
715
|
+
const injectedSelectorCache = new Map<
|
|
716
|
+
boolean,
|
|
717
|
+
WeakMap<
|
|
718
|
+
(rootState: any) => State | undefined,
|
|
719
|
+
Record<string, (rootState: any) => any>
|
|
720
|
+
>
|
|
721
|
+
>()
|
|
722
|
+
|
|
723
|
+
const injectedStateCache = new WeakMap<(rootState: any) => State, State>()
|
|
724
|
+
|
|
725
|
+
let _reducer: ReducerWithInitialState<State>
|
|
726
|
+
|
|
727
|
+
function reducer(state: State | undefined, action: UnknownAction) {
|
|
728
|
+
if (!_reducer) _reducer = buildReducer()
|
|
729
|
+
|
|
730
|
+
return _reducer(state, action)
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
function getInitialState() {
|
|
734
|
+
if (!_reducer) _reducer = buildReducer()
|
|
735
|
+
|
|
736
|
+
return _reducer.getInitialState()
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
function makeSelectorProps<CurrentReducerPath extends string = ReducerPath>(
|
|
740
|
+
reducerPath: CurrentReducerPath,
|
|
741
|
+
injected = false,
|
|
742
|
+
): Pick<
|
|
743
|
+
Slice<State, CaseReducers, Name, CurrentReducerPath, Selectors>,
|
|
744
|
+
'getSelectors' | 'selectors' | 'selectSlice' | 'reducerPath'
|
|
745
|
+
> {
|
|
746
|
+
function selectSlice(state: { [K in CurrentReducerPath]: State }) {
|
|
747
|
+
let sliceState = state[reducerPath]
|
|
748
|
+
if (typeof sliceState === 'undefined') {
|
|
749
|
+
if (injected) {
|
|
750
|
+
sliceState = getOrInsertComputed(
|
|
751
|
+
injectedStateCache,
|
|
752
|
+
selectSlice,
|
|
753
|
+
getInitialState,
|
|
754
|
+
)
|
|
755
|
+
} else if (process.env.NODE_ENV !== 'production') {
|
|
756
|
+
throw new Error(
|
|
757
|
+
'selectSlice returned undefined for an uninjected slice reducer',
|
|
758
|
+
)
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
return sliceState
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
function getSelectors(
|
|
765
|
+
selectState: (rootState: any) => State = selectSelf,
|
|
766
|
+
) {
|
|
767
|
+
const selectorCache = getOrInsertComputed(
|
|
768
|
+
injectedSelectorCache,
|
|
769
|
+
injected,
|
|
770
|
+
() => new WeakMap(),
|
|
771
|
+
)
|
|
772
|
+
|
|
773
|
+
return getOrInsertComputed(selectorCache, selectState, () => {
|
|
774
|
+
const map: Record<string, Selector<any, any>> = {}
|
|
775
|
+
for (const [name, selector] of Object.entries(
|
|
776
|
+
options.selectors ?? {},
|
|
777
|
+
)) {
|
|
778
|
+
map[name] = wrapSelector(
|
|
779
|
+
selector,
|
|
780
|
+
selectState,
|
|
781
|
+
() =>
|
|
782
|
+
getOrInsertComputed(
|
|
783
|
+
injectedStateCache,
|
|
784
|
+
selectState,
|
|
785
|
+
getInitialState,
|
|
786
|
+
),
|
|
787
|
+
injected,
|
|
788
|
+
)
|
|
789
|
+
}
|
|
790
|
+
return map
|
|
791
|
+
}) as any
|
|
792
|
+
}
|
|
793
|
+
return {
|
|
794
|
+
reducerPath,
|
|
795
|
+
getSelectors,
|
|
796
|
+
get selectors() {
|
|
797
|
+
return getSelectors(selectSlice)
|
|
798
|
+
},
|
|
799
|
+
selectSlice,
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
const slice: Slice<State, CaseReducers, Name, ReducerPath, Selectors> = {
|
|
804
|
+
name,
|
|
805
|
+
reducer,
|
|
806
|
+
actions: context.actionCreators as any,
|
|
807
|
+
caseReducers: context.sliceCaseReducersByName as any,
|
|
808
|
+
getInitialState,
|
|
809
|
+
...makeSelectorProps(reducerPath),
|
|
810
|
+
injectInto(injectable, { reducerPath: pathOpt, ...config } = {}) {
|
|
811
|
+
const newReducerPath = pathOpt ?? reducerPath
|
|
812
|
+
injectable.inject({ reducerPath: newReducerPath, reducer }, config)
|
|
813
|
+
return {
|
|
814
|
+
...slice,
|
|
815
|
+
...makeSelectorProps(newReducerPath, true),
|
|
816
|
+
} as any
|
|
817
|
+
},
|
|
818
|
+
}
|
|
819
|
+
return slice
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
function wrapSelector<State, NewState, S extends Selector<State>>(
|
|
824
|
+
selector: S,
|
|
825
|
+
selectState: Selector<NewState, State>,
|
|
826
|
+
getInitialState: () => State,
|
|
827
|
+
injected?: boolean,
|
|
828
|
+
) {
|
|
829
|
+
function wrapper(rootState: NewState, ...args: any[]) {
|
|
830
|
+
let sliceState = selectState(rootState)
|
|
831
|
+
if (typeof sliceState === 'undefined') {
|
|
832
|
+
if (injected) {
|
|
833
|
+
sliceState = getInitialState()
|
|
834
|
+
} else if (process.env.NODE_ENV !== 'production') {
|
|
835
|
+
throw new Error(
|
|
836
|
+
'selectState returned undefined for an uninjected slice reducer',
|
|
837
|
+
)
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
return selector(sliceState, ...args)
|
|
841
|
+
}
|
|
842
|
+
wrapper.unwrapped = selector
|
|
843
|
+
return wrapper as RemappedSelector<S, NewState>
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
/**
|
|
847
|
+
* A function that accepts an initial state, an object full of reducer
|
|
848
|
+
* functions, and a "slice name", and automatically generates
|
|
849
|
+
* action creators and action types that correspond to the
|
|
850
|
+
* reducers and state.
|
|
851
|
+
*
|
|
852
|
+
* @public
|
|
853
|
+
*/
|
|
854
|
+
export const createSlice = /* @__PURE__ */ buildCreateSlice()
|
|
855
|
+
|
|
856
|
+
interface ReducerHandlingContext<State> {
|
|
857
|
+
sliceCaseReducersByName: Record<
|
|
858
|
+
string,
|
|
859
|
+
| CaseReducer<State, any>
|
|
860
|
+
| Pick<
|
|
861
|
+
AsyncThunkSliceReducerDefinition<State, any, any, any>,
|
|
862
|
+
'fulfilled' | 'rejected' | 'pending' | 'settled'
|
|
863
|
+
>
|
|
864
|
+
>
|
|
865
|
+
sliceCaseReducersByType: Record<string, CaseReducer<State, any>>
|
|
866
|
+
sliceMatchers: ActionMatcherDescriptionCollection<State>
|
|
867
|
+
actionCreators: Record<string, Function>
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
interface ReducerHandlingContextMethods<State> {
|
|
871
|
+
/**
|
|
872
|
+
* Adds a case reducer to handle a single action type.
|
|
873
|
+
* @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.
|
|
874
|
+
* @param reducer - The actual case reducer function.
|
|
875
|
+
*/
|
|
876
|
+
addCase<ActionCreator extends TypedActionCreator<string>>(
|
|
877
|
+
actionCreator: ActionCreator,
|
|
878
|
+
reducer: CaseReducer<State, ReturnType<ActionCreator>>,
|
|
879
|
+
): ReducerHandlingContextMethods<State>
|
|
880
|
+
/**
|
|
881
|
+
* Adds a case reducer to handle a single action type.
|
|
882
|
+
* @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.
|
|
883
|
+
* @param reducer - The actual case reducer function.
|
|
884
|
+
*/
|
|
885
|
+
addCase<Type extends string, A extends Action<Type>>(
|
|
886
|
+
type: Type,
|
|
887
|
+
reducer: CaseReducer<State, A>,
|
|
888
|
+
): ReducerHandlingContextMethods<State>
|
|
889
|
+
|
|
890
|
+
/**
|
|
891
|
+
* Allows you to match incoming actions against your own filter function instead of only the `action.type` property.
|
|
892
|
+
* @remarks
|
|
893
|
+
* If multiple matcher reducers match, all of them will be executed in the order
|
|
894
|
+
* they were defined in - even if a case reducer already matched.
|
|
895
|
+
* All calls to `builder.addMatcher` must come after any calls to `builder.addCase` and before any calls to `builder.addDefaultCase`.
|
|
896
|
+
* @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)
|
|
897
|
+
* function
|
|
898
|
+
* @param reducer - The actual case reducer function.
|
|
899
|
+
*
|
|
900
|
+
*/
|
|
901
|
+
addMatcher<A>(
|
|
902
|
+
matcher: TypeGuard<A>,
|
|
903
|
+
reducer: CaseReducer<State, A extends Action ? A : A & Action>,
|
|
904
|
+
): ReducerHandlingContextMethods<State>
|
|
905
|
+
/**
|
|
906
|
+
* Add an action to be exposed under the final `slice.actions` key.
|
|
907
|
+
* @param name The key to be exposed as.
|
|
908
|
+
* @param actionCreator The action to expose.
|
|
909
|
+
* @example
|
|
910
|
+
* ```ts
|
|
911
|
+
* context.exposeAction('addPost', createAction<Post>('addPost'));
|
|
912
|
+
*
|
|
913
|
+
* export const { addPost } = slice.actions;
|
|
914
|
+
*
|
|
915
|
+
* dispatch(addPost(post));
|
|
916
|
+
* ```
|
|
917
|
+
*/
|
|
918
|
+
exposeAction(
|
|
919
|
+
name: string,
|
|
920
|
+
actionCreator: Function,
|
|
921
|
+
): ReducerHandlingContextMethods<State>
|
|
922
|
+
/**
|
|
923
|
+
* Add a case reducer to be exposed under the final `slice.caseReducers` key.
|
|
924
|
+
* @param name The key to be exposed as.
|
|
925
|
+
* @param reducer The reducer to expose.
|
|
926
|
+
* @example
|
|
927
|
+
* ```ts
|
|
928
|
+
* context.exposeCaseReducer('addPost', (state, action: PayloadAction<Post>) => {
|
|
929
|
+
* state.push(action.payload);
|
|
930
|
+
* });
|
|
931
|
+
*
|
|
932
|
+
* slice.caseReducers.addPost([], addPost(post));
|
|
933
|
+
* ```
|
|
934
|
+
*/
|
|
935
|
+
exposeCaseReducer(
|
|
936
|
+
name: string,
|
|
937
|
+
reducer:
|
|
938
|
+
| CaseReducer<State, any>
|
|
939
|
+
| Pick<
|
|
940
|
+
AsyncThunkSliceReducerDefinition<State, any, any, any>,
|
|
941
|
+
'fulfilled' | 'rejected' | 'pending' | 'settled'
|
|
942
|
+
>,
|
|
943
|
+
): ReducerHandlingContextMethods<State>
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
interface ReducerDetails {
|
|
947
|
+
/** The key the reducer was defined under */
|
|
948
|
+
reducerName: string
|
|
949
|
+
/** The predefined action type, i.e. `${slice.name}/${reducerName}` */
|
|
950
|
+
type: string
|
|
951
|
+
/** Whether create. notation was used when defining reducers */
|
|
952
|
+
createNotation: boolean
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
function buildReducerCreators<State>(): ReducerCreators<State> {
|
|
956
|
+
function asyncThunk(
|
|
957
|
+
payloadCreator: AsyncThunkPayloadCreator<any, any>,
|
|
958
|
+
config: AsyncThunkSliceReducerConfig<State, any>,
|
|
959
|
+
): AsyncThunkSliceReducerDefinition<State, any> {
|
|
960
|
+
return {
|
|
961
|
+
_reducerDefinitionType: ReducerType.asyncThunk,
|
|
962
|
+
payloadCreator,
|
|
963
|
+
...config,
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
asyncThunk.withTypes = () => asyncThunk
|
|
967
|
+
return {
|
|
968
|
+
reducer(caseReducer: CaseReducer<State, any>) {
|
|
969
|
+
return Object.assign(
|
|
970
|
+
{
|
|
971
|
+
// hack so the wrapping function has the same name as the original
|
|
972
|
+
// we need to create a wrapper so the `reducerDefinitionType` is not assigned to the original
|
|
973
|
+
[caseReducer.name](...args: Parameters<typeof caseReducer>) {
|
|
974
|
+
return caseReducer(...args)
|
|
975
|
+
},
|
|
976
|
+
}[caseReducer.name],
|
|
977
|
+
{
|
|
978
|
+
_reducerDefinitionType: ReducerType.reducer,
|
|
979
|
+
} as const,
|
|
980
|
+
)
|
|
981
|
+
},
|
|
982
|
+
preparedReducer(prepare, reducer) {
|
|
983
|
+
return {
|
|
984
|
+
_reducerDefinitionType: ReducerType.reducerWithPrepare,
|
|
985
|
+
prepare,
|
|
986
|
+
reducer,
|
|
987
|
+
}
|
|
988
|
+
},
|
|
989
|
+
asyncThunk: asyncThunk as any,
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
function handleNormalReducerDefinition<State>(
|
|
994
|
+
{ type, reducerName, createNotation }: ReducerDetails,
|
|
995
|
+
maybeReducerWithPrepare:
|
|
996
|
+
| CaseReducer<State, { payload: any; type: string }>
|
|
997
|
+
| CaseReducerWithPrepare<State, PayloadAction<any, string, any, any>>,
|
|
998
|
+
context: ReducerHandlingContextMethods<State>,
|
|
999
|
+
) {
|
|
1000
|
+
let caseReducer: CaseReducer<State, any>
|
|
1001
|
+
let prepareCallback: PrepareAction<any> | undefined
|
|
1002
|
+
if ('reducer' in maybeReducerWithPrepare) {
|
|
1003
|
+
if (
|
|
1004
|
+
createNotation &&
|
|
1005
|
+
!isCaseReducerWithPrepareDefinition(maybeReducerWithPrepare)
|
|
1006
|
+
) {
|
|
1007
|
+
throw new Error(
|
|
1008
|
+
'Please use the `create.preparedReducer` notation for prepared action creators with the `create` notation.',
|
|
1009
|
+
)
|
|
1010
|
+
}
|
|
1011
|
+
caseReducer = maybeReducerWithPrepare.reducer
|
|
1012
|
+
prepareCallback = maybeReducerWithPrepare.prepare
|
|
1013
|
+
} else {
|
|
1014
|
+
caseReducer = maybeReducerWithPrepare
|
|
1015
|
+
}
|
|
1016
|
+
context
|
|
1017
|
+
.addCase(type, caseReducer)
|
|
1018
|
+
.exposeCaseReducer(reducerName, caseReducer)
|
|
1019
|
+
.exposeAction(
|
|
1020
|
+
reducerName,
|
|
1021
|
+
prepareCallback
|
|
1022
|
+
? createAction(type, prepareCallback)
|
|
1023
|
+
: createAction(type),
|
|
1024
|
+
)
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
function isAsyncThunkSliceReducerDefinition<State>(
|
|
1028
|
+
reducerDefinition: any,
|
|
1029
|
+
): reducerDefinition is AsyncThunkSliceReducerDefinition<State, any, any, any> {
|
|
1030
|
+
return reducerDefinition._reducerDefinitionType === ReducerType.asyncThunk
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
function isCaseReducerWithPrepareDefinition<State>(
|
|
1034
|
+
reducerDefinition: any,
|
|
1035
|
+
): reducerDefinition is CaseReducerWithPrepareDefinition<State, any> {
|
|
1036
|
+
return (
|
|
1037
|
+
reducerDefinition._reducerDefinitionType === ReducerType.reducerWithPrepare
|
|
1038
|
+
)
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
function handleThunkCaseReducerDefinition<State>(
|
|
1042
|
+
{ type, reducerName }: ReducerDetails,
|
|
1043
|
+
reducerDefinition: AsyncThunkSliceReducerDefinition<State, any, any, any>,
|
|
1044
|
+
context: ReducerHandlingContextMethods<State>,
|
|
1045
|
+
cAT: typeof _createAsyncThunk | undefined,
|
|
1046
|
+
) {
|
|
1047
|
+
if (!cAT) {
|
|
1048
|
+
throw new Error(
|
|
1049
|
+
'Cannot use `create.asyncThunk` in the built-in `createSlice`. ' +
|
|
1050
|
+
'Use `buildCreateSlice({ creators: { asyncThunk: asyncThunkCreator } })` to create a customised version of `createSlice`.',
|
|
1051
|
+
)
|
|
1052
|
+
}
|
|
1053
|
+
const { payloadCreator, fulfilled, pending, rejected, settled, options } =
|
|
1054
|
+
reducerDefinition
|
|
1055
|
+
const thunk = cAT(type, payloadCreator, options as any)
|
|
1056
|
+
context.exposeAction(reducerName, thunk)
|
|
1057
|
+
|
|
1058
|
+
if (fulfilled) {
|
|
1059
|
+
context.addCase(thunk.fulfilled, fulfilled)
|
|
1060
|
+
}
|
|
1061
|
+
if (pending) {
|
|
1062
|
+
context.addCase(thunk.pending, pending)
|
|
1063
|
+
}
|
|
1064
|
+
if (rejected) {
|
|
1065
|
+
context.addCase(thunk.rejected, rejected)
|
|
1066
|
+
}
|
|
1067
|
+
if (settled) {
|
|
1068
|
+
context.addMatcher(thunk.settled, settled)
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
context.exposeCaseReducer(reducerName, {
|
|
1072
|
+
fulfilled: fulfilled || noop,
|
|
1073
|
+
pending: pending || noop,
|
|
1074
|
+
rejected: rejected || noop,
|
|
1075
|
+
settled: settled || noop,
|
|
1076
|
+
})
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
function noop() {}
|