@depup/reduxjs__toolkit 2.12.0-depup.29 → 2.13.0-depup.0
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/README.md +2 -2
- package/changes.json +1 -1
- package/dist/cjs/redux-toolkit.development.cjs +1755 -2247
- package/dist/cjs/redux-toolkit.development.cjs.map +1 -1
- package/dist/cjs/redux-toolkit.production.min.cjs +1 -2
- package/dist/cjs/redux-toolkit.production.min.cjs.map +1 -1
- package/dist/index.d.mts +1496 -1525
- package/dist/index.d.ts +1496 -1525
- package/dist/query/cjs/rtk-query.development.cjs +2059 -2990
- package/dist/query/cjs/rtk-query.development.cjs.map +1 -1
- package/dist/query/cjs/rtk-query.production.min.cjs +1 -1
- package/dist/query/cjs/rtk-query.production.min.cjs.map +1 -1
- package/dist/query/index.d.mts +2300 -2308
- package/dist/query/index.d.ts +2300 -2308
- package/dist/query/react/cjs/rtk-query-react.development.cjs +609 -714
- package/dist/query/react/cjs/rtk-query-react.development.cjs.map +1 -1
- package/dist/query/react/cjs/rtk-query-react.production.min.cjs +1 -1
- package/dist/query/react/cjs/rtk-query-react.production.min.cjs.map +1 -1
- package/dist/query/react/index.d.mts +516 -523
- package/dist/query/react/index.d.ts +516 -523
- package/dist/query/react/rtk-query-react.browser.mjs +1 -1
- package/dist/query/react/rtk-query-react.browser.mjs.map +1 -1
- package/dist/query/react/rtk-query-react.legacy-esm.js +672 -714
- package/dist/query/react/rtk-query-react.legacy-esm.js.map +1 -1
- package/dist/query/react/rtk-query-react.modern.mjs +586 -679
- package/dist/query/react/rtk-query-react.modern.mjs.map +1 -1
- package/dist/query/rtk-query.browser.mjs +1 -1
- package/dist/query/rtk-query.browser.mjs.map +1 -1
- package/dist/query/rtk-query.legacy-esm.js +2175 -3014
- package/dist/query/rtk-query.legacy-esm.js.map +1 -1
- package/dist/query/rtk-query.modern.mjs +2052 -2949
- package/dist/query/rtk-query.modern.mjs.map +1 -1
- package/dist/react/cjs/redux-toolkit-react.development.cjs +30 -51
- package/dist/react/cjs/redux-toolkit-react.development.cjs.map +1 -1
- package/dist/react/cjs/redux-toolkit-react.production.min.cjs +1 -1
- package/dist/react/cjs/redux-toolkit-react.production.min.cjs.map +1 -1
- package/dist/react/index.d.mts +12 -15
- package/dist/react/index.d.ts +12 -15
- package/dist/react/redux-toolkit-react.browser.mjs +1 -1
- package/dist/react/redux-toolkit-react.browser.mjs.map +1 -1
- package/dist/react/redux-toolkit-react.legacy-esm.js +83 -44
- package/dist/react/redux-toolkit-react.legacy-esm.js.map +1 -1
- package/dist/react/redux-toolkit-react.modern.mjs +23 -25
- package/dist/react/redux-toolkit-react.modern.mjs.map +1 -1
- package/dist/redux-toolkit.browser.mjs +1 -2
- package/dist/redux-toolkit.browser.mjs.map +1 -1
- package/dist/redux-toolkit.legacy-esm.js +1748 -2201
- package/dist/redux-toolkit.legacy-esm.js.map +1 -1
- package/dist/redux-toolkit.modern.mjs +1670 -2171
- package/dist/redux-toolkit.modern.mjs.map +1 -1
- package/package.json +13 -29
- package/skills/build-modern-redux-apps/modern-redux/SKILL.md +23 -14
- package/skills/build-modern-redux-apps/modern-redux/references/store-lifetime.md +6 -6
- package/skills/build-modern-redux-apps/redux-dataflow/SKILL.md +13 -10
- package/skills/evolve-and-diagnose-redux-apps/debug-redux-toolkit-apps/SKILL.md +6 -6
- package/skills/evolve-and-diagnose-redux-apps/migrate-to-modern-redux/SKILL.md +7 -8
- package/skills/manage-server-data/adopt-rtk-query/SKILL.md +13 -11
- package/skills/model-redux-state/build-slices-and-selectors/SKILL.md +26 -14
- package/skills/model-redux-state/design-state-ownership/SKILL.md +11 -12
- package/skills/model-redux-state/design-state-ownership/references/state-ownership.md +6 -6
- package/skills/orchestrate-side-effects/handle-side-effects/SKILL.md +27 -23
- package/skills/orchestrate-side-effects/handle-side-effects/references/listener-workflows.md +4 -4
- package/src/autoBatchEnhancer.ts +5 -3
- package/src/combineSlices.ts +5 -5
- package/src/createAction.ts +11 -6
- package/src/createAsyncThunk.ts +14 -2
- package/src/createReducer.ts +1 -1
- package/src/createSlice.ts +9 -4
- package/src/dynamicMiddleware/index.ts +21 -5
- package/src/entities/models.ts +2 -1
- package/src/entities/sorted_state_adapter.ts +35 -24
- package/src/entities/unsorted_state_adapter.ts +1 -1
- package/src/immutableStateInvariantMiddleware.ts +34 -5
- package/src/listenerMiddleware/types.ts +10 -10
- package/src/mapBuilders.ts +2 -5
- package/src/matchers.ts +7 -1
- package/src/query/core/apiState.ts +1 -1
- package/src/query/core/buildInitiate.ts +19 -15
- package/src/query/core/buildMiddleware/cacheLifecycle.ts +3 -1
- package/src/query/core/buildMiddleware/index.ts +1 -1
- package/src/query/core/buildMiddleware/invalidationByTags.ts +4 -1
- package/src/query/core/buildMiddleware/polling.ts +12 -3
- package/src/query/core/buildMiddleware/types.ts +5 -2
- package/src/query/core/buildSlice.ts +2 -2
- package/src/query/core/buildThunks.ts +19 -0
- package/src/query/core/module.ts +9 -3
- package/src/query/createApi.ts +4 -1
- package/src/query/endpointDefinitions.ts +15 -9
- package/src/query/fetchBaseQuery.ts +1 -1
- package/src/query/react/buildHooks.ts +54 -51
- package/src/query/react/namedHooks.ts +28 -24
- package/src/query/utils/isAbsoluteUrl.ts +8 -3
- package/src/serializableStateInvariantMiddleware.ts +1 -1
package/src/combineSlices.ts
CHANGED
|
@@ -62,9 +62,8 @@ export type InjectConfig = {
|
|
|
62
62
|
export interface CombinedSliceReducer<
|
|
63
63
|
InitialState,
|
|
64
64
|
DeclaredState extends InitialState = InitialState,
|
|
65
|
-
PreloadedState extends Partial<
|
|
66
|
-
|
|
67
|
-
> = Partial<DeclaredState>,
|
|
65
|
+
PreloadedState extends Partial<Record<keyof PreloadedState, any>> =
|
|
66
|
+
Partial<DeclaredState>,
|
|
68
67
|
> extends Reducer<DeclaredState, UnknownAction, PreloadedState> {
|
|
69
68
|
/**
|
|
70
69
|
* Provide a type for slices that will be injected lazily.
|
|
@@ -354,9 +353,8 @@ const ORIGINAL_STATE = Symbol.for('rtk-state-proxy-original')
|
|
|
354
353
|
|
|
355
354
|
const isStateProxy = (value: any) => !!value && !!value[ORIGINAL_STATE]
|
|
356
355
|
|
|
357
|
-
const stateProxyMap = new WeakMap<object, object>()
|
|
358
|
-
|
|
359
356
|
const createStateProxy = <State extends object>(
|
|
357
|
+
stateProxyMap: WeakMap<object, object>,
|
|
360
358
|
state: State,
|
|
361
359
|
reducerMap: Partial<Record<PropertyKey, Reducer>>,
|
|
362
360
|
initialStateCache: Record<PropertyKey, unknown>,
|
|
@@ -411,6 +409,7 @@ export function combineSlices<Slices extends Array<AnySliceLike | ReducerMap>>(
|
|
|
411
409
|
Id<InitialState<Slices>>,
|
|
412
410
|
Partial<Id<InitialPreloadedState<Slices>>>
|
|
413
411
|
> {
|
|
412
|
+
const stateProxyMap = new WeakMap<object, object>()
|
|
414
413
|
const reducerMap = Object.fromEntries(getReducers(slices))
|
|
415
414
|
|
|
416
415
|
const getReducer = () =>
|
|
@@ -472,6 +471,7 @@ export function combineSlices<Slices extends Array<AnySliceLike | ReducerMap>>(
|
|
|
472
471
|
return function selector(state: State, ...args: Args) {
|
|
473
472
|
return selectorFn(
|
|
474
473
|
createStateProxy(
|
|
474
|
+
stateProxyMap,
|
|
475
475
|
selectState ? selectState(state as any, ...args) : state,
|
|
476
476
|
reducerMap,
|
|
477
477
|
initialStateCache,
|
package/src/createAction.ts
CHANGED
|
@@ -122,8 +122,10 @@ export interface ActionCreatorWithPreparedPayload<
|
|
|
122
122
|
*
|
|
123
123
|
* @public
|
|
124
124
|
*/
|
|
125
|
-
export interface ActionCreatorWithOptionalPayload<
|
|
126
|
-
|
|
125
|
+
export interface ActionCreatorWithOptionalPayload<
|
|
126
|
+
P,
|
|
127
|
+
T extends string = string,
|
|
128
|
+
> extends BaseActionCreator<P, T> {
|
|
127
129
|
/**
|
|
128
130
|
* Calling this {@link redux#ActionCreator} with an argument will
|
|
129
131
|
* return a {@link PayloadAction} of type `T` with a payload of `P`.
|
|
@@ -139,8 +141,9 @@ export interface ActionCreatorWithOptionalPayload<P, T extends string = string>
|
|
|
139
141
|
*
|
|
140
142
|
* @public
|
|
141
143
|
*/
|
|
142
|
-
export interface ActionCreatorWithoutPayload<
|
|
143
|
-
extends
|
|
144
|
+
export interface ActionCreatorWithoutPayload<
|
|
145
|
+
T extends string = string,
|
|
146
|
+
> extends BaseActionCreator<undefined, T> {
|
|
144
147
|
/**
|
|
145
148
|
* Calling this {@link redux#ActionCreator} will
|
|
146
149
|
* return a {@link PayloadAction} of type `T` with a payload of `undefined`
|
|
@@ -155,8 +158,10 @@ export interface ActionCreatorWithoutPayload<T extends string = string>
|
|
|
155
158
|
*
|
|
156
159
|
* @public
|
|
157
160
|
*/
|
|
158
|
-
export interface ActionCreatorWithPayload<
|
|
159
|
-
|
|
161
|
+
export interface ActionCreatorWithPayload<
|
|
162
|
+
P,
|
|
163
|
+
T extends string = string,
|
|
164
|
+
> extends BaseActionCreator<P, T> {
|
|
160
165
|
/**
|
|
161
166
|
* Calling this {@link redux#ActionCreator} with an argument will
|
|
162
167
|
* return a {@link PayloadAction} of type `T` with a payload of `P`
|
package/src/createAsyncThunk.ts
CHANGED
|
@@ -590,7 +590,7 @@ export const createAsyncThunk = /* @__PURE__ */ (() => {
|
|
|
590
590
|
...((meta as any) || {}),
|
|
591
591
|
arg,
|
|
592
592
|
requestId,
|
|
593
|
-
rejectedWithValue:
|
|
593
|
+
rejectedWithValue: payload !== undefined,
|
|
594
594
|
requestStatus: 'rejected' as const,
|
|
595
595
|
aborted: error?.name === 'AbortError',
|
|
596
596
|
condition: error?.name === 'ConditionError',
|
|
@@ -636,13 +636,25 @@ export const createAsyncThunk = /* @__PURE__ */ (() => {
|
|
|
636
636
|
conditionResult = await conditionResult
|
|
637
637
|
}
|
|
638
638
|
|
|
639
|
-
if (conditionResult === false
|
|
639
|
+
if (conditionResult === false) {
|
|
640
640
|
// eslint-disable-next-line no-throw-literal
|
|
641
641
|
throw {
|
|
642
642
|
name: 'ConditionError',
|
|
643
643
|
message: 'Aborted due to condition callback returning false.',
|
|
644
644
|
}
|
|
645
645
|
}
|
|
646
|
+
if (abortController.signal.aborted) {
|
|
647
|
+
// The request was aborted (e.g. via an already-aborted external
|
|
648
|
+
// signal, or `abort()` called during an async `condition`) before
|
|
649
|
+
// the `pending` action was dispatched. Treat this as an abort
|
|
650
|
+
// rather than a condition rejection, so the reason is preserved
|
|
651
|
+
// and the `rejected` action is not silently swallowed.
|
|
652
|
+
// eslint-disable-next-line no-throw-literal
|
|
653
|
+
throw {
|
|
654
|
+
name: 'AbortError',
|
|
655
|
+
message: abortReason || 'Aborted',
|
|
656
|
+
}
|
|
657
|
+
}
|
|
646
658
|
|
|
647
659
|
const abortedPromise = new Promise<never>((_, reject) => {
|
|
648
660
|
abortHandler = () => {
|
package/src/createReducer.ts
CHANGED
package/src/createSlice.ts
CHANGED
|
@@ -298,7 +298,9 @@ export type CaseReducerWithPrepare<State, Action extends PayloadAction> = {
|
|
|
298
298
|
export interface CaseReducerWithPrepareDefinition<
|
|
299
299
|
State,
|
|
300
300
|
Action extends PayloadAction,
|
|
301
|
-
>
|
|
301
|
+
>
|
|
302
|
+
extends
|
|
303
|
+
CaseReducerWithPrepare<State, Action>,
|
|
302
304
|
ReducerDefinition<ReducerType.reducerWithPrepare> {}
|
|
303
305
|
|
|
304
306
|
type AsyncThunkSliceReducerConfig<
|
|
@@ -331,8 +333,8 @@ type PreventCircular<ThunkApiConfig> = {
|
|
|
331
333
|
|
|
332
334
|
interface AsyncThunkCreator<
|
|
333
335
|
State,
|
|
334
|
-
CurriedThunkApiConfig extends
|
|
335
|
-
PreventCircular<AsyncThunkConfig
|
|
336
|
+
CurriedThunkApiConfig extends PreventCircular<AsyncThunkConfig> =
|
|
337
|
+
PreventCircular<AsyncThunkConfig>,
|
|
336
338
|
> {
|
|
337
339
|
<Returned, ThunkArg = void>(
|
|
338
340
|
payloadCreator: AsyncThunkPayloadCreator<
|
|
@@ -807,7 +809,10 @@ export function buildCreateSlice({ creators }: BuildCreateSliceConfig = {}) {
|
|
|
807
809
|
caseReducers: context.sliceCaseReducersByName as any,
|
|
808
810
|
getInitialState,
|
|
809
811
|
...makeSelectorProps(reducerPath),
|
|
810
|
-
injectInto(
|
|
812
|
+
injectInto(
|
|
813
|
+
injectable,
|
|
814
|
+
{ reducerPath: pathOpt, ...config }: InjectIntoConfig<string> = {},
|
|
815
|
+
) {
|
|
811
816
|
const newReducerPath = pathOpt ?? reducerPath
|
|
812
817
|
injectable.inject({ reducerPath: newReducerPath, reducer }, config)
|
|
813
818
|
return {
|
|
@@ -41,6 +41,7 @@ export const createDynamicMiddleware = <
|
|
|
41
41
|
Middleware<any, State, DispatchType>,
|
|
42
42
|
MiddlewareEntry<State, DispatchType>
|
|
43
43
|
>()
|
|
44
|
+
let middlewareVersion = 0
|
|
44
45
|
|
|
45
46
|
const withMiddleware = Object.assign(
|
|
46
47
|
createAction(
|
|
@@ -59,9 +60,13 @@ export const createDynamicMiddleware = <
|
|
|
59
60
|
function addMiddleware(
|
|
60
61
|
...middlewares: Middleware<any, State, DispatchType>[]
|
|
61
62
|
) {
|
|
63
|
+
const previousSize = middlewareMap.size
|
|
62
64
|
middlewares.forEach((middleware) => {
|
|
63
65
|
getOrInsertComputed(middlewareMap, middleware, createMiddlewareEntry)
|
|
64
66
|
})
|
|
67
|
+
if (middlewareMap.size !== previousSize) {
|
|
68
|
+
middlewareVersion++
|
|
69
|
+
}
|
|
65
70
|
},
|
|
66
71
|
{ withTypes: () => addMiddleware },
|
|
67
72
|
) as AddMiddleware<State, DispatchType>
|
|
@@ -76,12 +81,23 @@ export const createDynamicMiddleware = <
|
|
|
76
81
|
const isWithMiddleware = isAllOf(withMiddleware, matchInstance(instanceId))
|
|
77
82
|
|
|
78
83
|
const middleware: DynamicMiddleware<State, DispatchType> =
|
|
79
|
-
(api) => (next) =>
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
84
|
+
(api) => (next) => {
|
|
85
|
+
let appliedVersion = -1
|
|
86
|
+
let dispatch = next
|
|
87
|
+
|
|
88
|
+
return (action) => {
|
|
89
|
+
if (isWithMiddleware(action)) {
|
|
90
|
+
addMiddleware(...action.payload)
|
|
91
|
+
return api.dispatch
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (appliedVersion !== middlewareVersion) {
|
|
95
|
+
dispatch = getFinalMiddleware(api)(next)
|
|
96
|
+
appliedVersion = middlewareVersion
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return dispatch(action)
|
|
83
100
|
}
|
|
84
|
-
return getFinalMiddleware(api)(next)(action)
|
|
85
101
|
}
|
|
86
102
|
|
|
87
103
|
return {
|
package/src/entities/models.ts
CHANGED
|
@@ -184,7 +184,8 @@ export interface EntityStateFactory<T, Id extends EntityId> {
|
|
|
184
184
|
* @public
|
|
185
185
|
*/
|
|
186
186
|
export interface EntityAdapter<T, Id extends EntityId>
|
|
187
|
-
extends
|
|
187
|
+
extends
|
|
188
|
+
EntityStateAdapter<T, Id>,
|
|
188
189
|
EntityStateFactory<T, Id>,
|
|
189
190
|
Required<EntityAdapterOptions<T, Id>> {
|
|
190
191
|
getSelectors(
|
|
@@ -70,15 +70,13 @@ export function createSortedStateAdapter<T, Id extends EntityId>(
|
|
|
70
70
|
newEntities = ensureEntitiesArray(newEntities)
|
|
71
71
|
|
|
72
72
|
const existingKeys = new Set<Id>(existingIds ?? getCurrent(state.ids))
|
|
73
|
-
const addedKeys = new Set<Id>()
|
|
74
|
-
const models = newEntities.filter(
|
|
75
|
-
(model)
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
81
|
-
)
|
|
73
|
+
const addedKeys = new Set<Id>()
|
|
74
|
+
const models = newEntities.filter((model) => {
|
|
75
|
+
const modelId = selectIdValue(model, selectId)
|
|
76
|
+
const notAdded = !addedKeys.has(modelId)
|
|
77
|
+
if (notAdded) addedKeys.add(modelId)
|
|
78
|
+
return !existingKeys.has(modelId) && notAdded
|
|
79
|
+
})
|
|
82
80
|
|
|
83
81
|
if (models.length !== 0) {
|
|
84
82
|
mergeFunction(state, models)
|
|
@@ -93,16 +91,16 @@ export function createSortedStateAdapter<T, Id extends EntityId>(
|
|
|
93
91
|
newEntities: readonly T[] | Record<Id, T>,
|
|
94
92
|
state: R,
|
|
95
93
|
): void {
|
|
96
|
-
let deduplicatedEntities = {} as Record<Id, T
|
|
94
|
+
let deduplicatedEntities = {} as Record<Id, T>
|
|
97
95
|
newEntities = ensureEntitiesArray(newEntities)
|
|
98
96
|
if (newEntities.length !== 0) {
|
|
99
97
|
for (const item of newEntities) {
|
|
100
|
-
const entityId = selectId(item)
|
|
98
|
+
const entityId = selectId(item)
|
|
101
99
|
// For multiple items with the same ID, we should keep the last one.
|
|
102
|
-
deduplicatedEntities[entityId] = item
|
|
100
|
+
deduplicatedEntities[entityId] = item
|
|
103
101
|
delete (state.entities as Record<Id, T>)[entityId]
|
|
104
102
|
}
|
|
105
|
-
newEntities = ensureEntitiesArray(deduplicatedEntities)
|
|
103
|
+
newEntities = ensureEntitiesArray(deduplicatedEntities)
|
|
106
104
|
mergeFunction(state, newEntities)
|
|
107
105
|
}
|
|
108
106
|
}
|
|
@@ -115,7 +113,7 @@ export function createSortedStateAdapter<T, Id extends EntityId>(
|
|
|
115
113
|
state.entities = {} as Record<Id, T>
|
|
116
114
|
state.ids = []
|
|
117
115
|
|
|
118
|
-
|
|
116
|
+
setManyMutably(newEntities, state)
|
|
119
117
|
}
|
|
120
118
|
|
|
121
119
|
function updateOneMutably(update: Update<T, Id>, state: R): void {
|
|
@@ -129,7 +127,27 @@ export function createSortedStateAdapter<T, Id extends EntityId>(
|
|
|
129
127
|
let appliedUpdates = false
|
|
130
128
|
let replacedIds = false
|
|
131
129
|
|
|
132
|
-
|
|
130
|
+
// Pre-merge all updates that target the same entity ID so that later
|
|
131
|
+
// changes always win for every field—including `id`. Without this step,
|
|
132
|
+
// the first rename moves the entity to a new key; a subsequent update that
|
|
133
|
+
// still targets the original key finds nothing and is silently dropped,
|
|
134
|
+
// producing results inconsistent with the unsorted adapter.
|
|
135
|
+
const updatesPerEntity: { [id: string]: Update<T, Id> } = {}
|
|
136
|
+
for (const update of updates) {
|
|
137
|
+
// Only collect updates for entities that currently exist (same guard
|
|
138
|
+
// used by the unsorted adapter).
|
|
139
|
+
if (update.id in (state.entities as Record<Id, T>)) {
|
|
140
|
+
updatesPerEntity[update.id] = {
|
|
141
|
+
id: update.id,
|
|
142
|
+
changes: {
|
|
143
|
+
...updatesPerEntity[update.id]?.changes,
|
|
144
|
+
...update.changes,
|
|
145
|
+
},
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
for (const update of Object.values(updatesPerEntity) as Update<T, Id>[]) {
|
|
133
151
|
const entity: T | undefined = (state.entities as Record<Id, T>)[update.id]
|
|
134
152
|
if (!entity) {
|
|
135
153
|
continue
|
|
@@ -192,19 +210,12 @@ export function createSortedStateAdapter<T, Id extends EntityId>(
|
|
|
192
210
|
return true
|
|
193
211
|
}
|
|
194
212
|
|
|
195
|
-
|
|
213
|
+
function mergeFunction(
|
|
196
214
|
state: R,
|
|
197
215
|
addedItems: readonly T[],
|
|
198
216
|
appliedUpdates?: boolean,
|
|
199
217
|
replacedIds?: boolean,
|
|
200
|
-
)
|
|
201
|
-
|
|
202
|
-
const mergeFunction: MergeFunction = (
|
|
203
|
-
state,
|
|
204
|
-
addedItems,
|
|
205
|
-
appliedUpdates,
|
|
206
|
-
replacedIds,
|
|
207
|
-
) => {
|
|
218
|
+
): void {
|
|
208
219
|
const currentEntities = getCurrent(state.entities)
|
|
209
220
|
const currentIds = getCurrent(state.ids)
|
|
210
221
|
|
|
@@ -70,7 +70,7 @@ export function createUnsortedStateAdapter<T, Id extends EntityId>(
|
|
|
70
70
|
state.ids = []
|
|
71
71
|
state.entities = {} as Record<Id, T>
|
|
72
72
|
|
|
73
|
-
|
|
73
|
+
setManyMutably(newEntities, state)
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
function removeOneMutably(key: Id, state: R): void {
|
|
@@ -36,13 +36,20 @@ function trackProperties(
|
|
|
36
36
|
ignoredPaths: IgnoredPaths = [],
|
|
37
37
|
obj: Record<string, any>,
|
|
38
38
|
path: string = '',
|
|
39
|
-
|
|
39
|
+
inProgress: Map<Record<string, any>, TrackedProperty> = new Map(),
|
|
40
40
|
) {
|
|
41
41
|
const tracked: Partial<TrackedProperty> = { value: obj }
|
|
42
42
|
|
|
43
|
-
if (!isImmutable(obj)
|
|
44
|
-
|
|
43
|
+
if (!isImmutable(obj)) {
|
|
44
|
+
// An object already being tracked further up the stack contains itself
|
|
45
|
+
const alreadyInProgress = inProgress.get(obj)
|
|
46
|
+
if (alreadyInProgress) {
|
|
47
|
+
return alreadyInProgress
|
|
48
|
+
}
|
|
49
|
+
|
|
45
50
|
tracked.children = {}
|
|
51
|
+
// Registered before walking children, so a circular reference finds it
|
|
52
|
+
inProgress.set(obj, tracked as TrackedProperty)
|
|
46
53
|
|
|
47
54
|
const hasIgnoredPaths = ignoredPaths.length > 0
|
|
48
55
|
|
|
@@ -66,8 +73,11 @@ function trackProperties(
|
|
|
66
73
|
ignoredPaths,
|
|
67
74
|
obj[key],
|
|
68
75
|
nestedPath,
|
|
76
|
+
inProgress,
|
|
69
77
|
)
|
|
70
78
|
}
|
|
79
|
+
|
|
80
|
+
inProgress.delete(obj)
|
|
71
81
|
}
|
|
72
82
|
return tracked as TrackedProperty
|
|
73
83
|
}
|
|
@@ -79,6 +89,7 @@ function detectMutations(
|
|
|
79
89
|
obj: any,
|
|
80
90
|
sameParentRef: boolean = false,
|
|
81
91
|
path: string = '',
|
|
92
|
+
seen: Map<TrackedProperty, Set<unknown>> = new Map(),
|
|
82
93
|
): { wasMutated: boolean; path?: string } {
|
|
83
94
|
const prevObj = trackedProperty ? trackedProperty.value : undefined
|
|
84
95
|
|
|
@@ -92,6 +103,18 @@ function detectMutations(
|
|
|
92
103
|
return { wasMutated: false }
|
|
93
104
|
}
|
|
94
105
|
|
|
106
|
+
// Compare each (tracked node, value) pair once, so cycles terminate
|
|
107
|
+
let seenValues = seen.get(trackedProperty)
|
|
108
|
+
|
|
109
|
+
if (!seenValues) {
|
|
110
|
+
seenValues = new Set()
|
|
111
|
+
seen.set(trackedProperty, seenValues)
|
|
112
|
+
} else if (seenValues.has(obj)) {
|
|
113
|
+
return { wasMutated: false }
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
seenValues.add(obj)
|
|
117
|
+
|
|
95
118
|
// Gather all keys from prev (tracked) and after objs
|
|
96
119
|
const keysToDetect: Record<string, boolean> = {}
|
|
97
120
|
for (let key in trackedProperty.children) {
|
|
@@ -125,6 +148,7 @@ function detectMutations(
|
|
|
125
148
|
obj[key],
|
|
126
149
|
sameRef,
|
|
127
150
|
nestedPath,
|
|
151
|
+
seen,
|
|
128
152
|
)
|
|
129
153
|
|
|
130
154
|
if (result.wasMutated) {
|
|
@@ -201,8 +225,13 @@ export function createImmutableStateInvariantMiddleware(
|
|
|
201
225
|
return function (this: any, key: string, value: any) {
|
|
202
226
|
if (stack.length > 0) {
|
|
203
227
|
var thisPos = stack.indexOf(this)
|
|
204
|
-
|
|
205
|
-
|
|
228
|
+
if (~thisPos) {
|
|
229
|
+
stack.splice(thisPos + 1)
|
|
230
|
+
keys.splice(thisPos, Infinity, key)
|
|
231
|
+
} else {
|
|
232
|
+
stack.push(this)
|
|
233
|
+
keys.push(key)
|
|
234
|
+
}
|
|
206
235
|
if (~stack.indexOf(value)) value = decycler!.call(this, key, value)
|
|
207
236
|
} else stack.push(value)
|
|
208
237
|
|
|
@@ -36,7 +36,6 @@ export type ListenerPredicate<ActionType extends Action, State> = (
|
|
|
36
36
|
|
|
37
37
|
/** @public */
|
|
38
38
|
export interface ConditionFunction<State> {
|
|
39
|
-
(predicate: AnyListenerPredicate<State>, timeout?: number): Promise<boolean>
|
|
40
39
|
(predicate: AnyListenerPredicate<State>, timeout?: number): Promise<boolean>
|
|
41
40
|
(predicate: () => boolean, timeout?: number): Promise<boolean>
|
|
42
41
|
}
|
|
@@ -157,7 +156,7 @@ export interface ListenerEffectAPI<
|
|
|
157
156
|
*
|
|
158
157
|
* setTimeout(getOriginalState, 0); // async: throws Error
|
|
159
158
|
*
|
|
160
|
-
* await Promise
|
|
159
|
+
* await Promise.resolve();
|
|
161
160
|
*
|
|
162
161
|
* getOriginalState(); // async: throws Error
|
|
163
162
|
* },
|
|
@@ -324,11 +323,8 @@ export type ListenerMiddleware<
|
|
|
324
323
|
/** @public */
|
|
325
324
|
export interface ListenerMiddlewareInstance<
|
|
326
325
|
StateType = unknown,
|
|
327
|
-
DispatchType extends ThunkDispatch<
|
|
328
|
-
StateType,
|
|
329
|
-
unknown,
|
|
330
|
-
Action
|
|
331
|
-
> = ThunkDispatch<StateType, unknown, UnknownAction>,
|
|
326
|
+
DispatchType extends ThunkDispatch<StateType, unknown, Action> =
|
|
327
|
+
ThunkDispatch<StateType, unknown, UnknownAction>,
|
|
332
328
|
ExtraArgument = unknown,
|
|
333
329
|
> {
|
|
334
330
|
middleware: ListenerMiddleware<StateType, DispatchType, ExtraArgument>
|
|
@@ -833,7 +829,7 @@ export type TypedCreateListenerEntry<
|
|
|
833
829
|
UnknownAction
|
|
834
830
|
>,
|
|
835
831
|
OverrideExtraArgument = unknown,
|
|
836
|
-
>() =>
|
|
832
|
+
>() => TypedCreateListenerEntry<
|
|
837
833
|
OverrideStateType,
|
|
838
834
|
OverrideDispatchType,
|
|
839
835
|
OverrideExtraArgument
|
|
@@ -870,14 +866,18 @@ export type FallbackAddListenerOptions = {
|
|
|
870
866
|
type?: string
|
|
871
867
|
matcher?: MatchFunction<any>
|
|
872
868
|
predicate?: ListenerPredicate<any, any>
|
|
873
|
-
|
|
869
|
+
effect: ListenerEffect<any, any, any>
|
|
870
|
+
}
|
|
874
871
|
|
|
875
872
|
/**
|
|
876
873
|
* Utility Types
|
|
877
874
|
*/
|
|
878
875
|
|
|
879
876
|
/** @public */
|
|
880
|
-
export type GuardedType<T> = T extends (
|
|
877
|
+
export type GuardedType<T> = T extends ((
|
|
878
|
+
x: any,
|
|
879
|
+
...args: any[]
|
|
880
|
+
) => x is infer T)
|
|
881
881
|
? T
|
|
882
882
|
: never
|
|
883
883
|
|
package/src/mapBuilders.ts
CHANGED
|
@@ -200,7 +200,7 @@ export function executeReducerBuilderCallback<S>(
|
|
|
200
200
|
const actionsMap: CaseReducers<S, any> = {}
|
|
201
201
|
const actionMatchers: ActionMatcherDescriptionCollection<S> = []
|
|
202
202
|
let defaultCaseReducer: CaseReducer<S, Action> | undefined
|
|
203
|
-
const builder = {
|
|
203
|
+
const builder: ActionReducerMapBuilder<S> = {
|
|
204
204
|
addCase(
|
|
205
205
|
typeOrActionCreator: string | TypedActionCreator<any>,
|
|
206
206
|
reducer: CaseReducer<S>,
|
|
@@ -269,10 +269,7 @@ export function executeReducerBuilderCallback<S>(
|
|
|
269
269
|
})
|
|
270
270
|
return builder
|
|
271
271
|
},
|
|
272
|
-
addMatcher<
|
|
273
|
-
matcher: TypeGuard<A>,
|
|
274
|
-
reducer: CaseReducer<S, A extends Action ? A : A & Action>,
|
|
275
|
-
) {
|
|
272
|
+
addMatcher(matcher: TypeGuard<any>, reducer: CaseReducer<S, any>) {
|
|
276
273
|
if (process.env.NODE_ENV !== 'production') {
|
|
277
274
|
if (defaultCaseReducer) {
|
|
278
275
|
throw new Error(
|
package/src/matchers.ts
CHANGED
|
@@ -361,5 +361,11 @@ export function isAsyncThunkAction<
|
|
|
361
361
|
return isAsyncThunkAction()(asyncThunks[0])
|
|
362
362
|
}
|
|
363
363
|
|
|
364
|
-
return isAnyOf(
|
|
364
|
+
return isAnyOf(
|
|
365
|
+
...asyncThunks.flatMap((asyncThunk) => [
|
|
366
|
+
asyncThunk.pending,
|
|
367
|
+
asyncThunk.rejected,
|
|
368
|
+
asyncThunk.fulfilled,
|
|
369
|
+
]),
|
|
370
|
+
)
|
|
365
371
|
}
|
|
@@ -354,7 +354,7 @@ export type SubscriptionState = {
|
|
|
354
354
|
[queryCacheKey: string]: Subscribers | undefined
|
|
355
355
|
}
|
|
356
356
|
|
|
357
|
-
export type ConfigState<ReducerPath> =
|
|
357
|
+
export type ConfigState<ReducerPath> = {
|
|
358
358
|
reducerPath: ReducerPath
|
|
359
359
|
online: boolean
|
|
360
360
|
focused: boolean
|
|
@@ -13,7 +13,6 @@ import type { InternalSerializeQueryArgs } from '../defaultSerializeQueryArgs'
|
|
|
13
13
|
import {
|
|
14
14
|
ENDPOINT_QUERY,
|
|
15
15
|
isQueryDefinition,
|
|
16
|
-
type EndpointDefinition,
|
|
17
16
|
type EndpointDefinitions,
|
|
18
17
|
type InfiniteQueryArgFrom,
|
|
19
18
|
type InfiniteQueryDefinition,
|
|
@@ -96,7 +95,7 @@ export type StartInfiniteQueryActionCreatorOptions<
|
|
|
96
95
|
>
|
|
97
96
|
>
|
|
98
97
|
|
|
99
|
-
type AnyQueryActionCreator
|
|
98
|
+
type AnyQueryActionCreator = (
|
|
100
99
|
arg: any,
|
|
101
100
|
options?: StartQueryActionCreatorOptions,
|
|
102
101
|
) => ThunkAction<AnyActionCreatorResult, any, any, UnknownAction>
|
|
@@ -154,20 +153,22 @@ export type InfiniteQueryActionCreatorResult<
|
|
|
154
153
|
): InfiniteQueryActionCreatorResult<D>
|
|
155
154
|
}
|
|
156
155
|
|
|
156
|
+
type StartMutationActionCreatorOptions = {
|
|
157
|
+
/**
|
|
158
|
+
* If this mutation should be tracked in the store.
|
|
159
|
+
* If you just want to manually trigger this mutation using `dispatch` and don't care about the
|
|
160
|
+
* result, state & potential errors being held in store, you can set this to false.
|
|
161
|
+
* (defaults to `true`)
|
|
162
|
+
*/
|
|
163
|
+
track?: boolean
|
|
164
|
+
fixedCacheKey?: string
|
|
165
|
+
}
|
|
166
|
+
|
|
157
167
|
type StartMutationActionCreator<
|
|
158
168
|
D extends MutationDefinition<any, any, any, any>,
|
|
159
169
|
> = (
|
|
160
170
|
arg: QueryArgFrom<D>,
|
|
161
|
-
options?:
|
|
162
|
-
/**
|
|
163
|
-
* If this mutation should be tracked in the store.
|
|
164
|
-
* If you just want to manually trigger this mutation using `dispatch` and don't care about the
|
|
165
|
-
* result, state & potential errors being held in store, you can set this to false.
|
|
166
|
-
* (defaults to `true`)
|
|
167
|
-
*/
|
|
168
|
-
track?: boolean
|
|
169
|
-
fixedCacheKey?: string
|
|
170
|
-
},
|
|
171
|
+
options?: StartMutationActionCreatorOptions,
|
|
171
172
|
) => ThunkAction<MutationActionCreatorResult<D>, any, any, UnknownAction>
|
|
172
173
|
|
|
173
174
|
export type MutationActionCreatorResult<
|
|
@@ -381,7 +382,7 @@ You must add the middleware for RTK-Query to function correctly!`,
|
|
|
381
382
|
| QueryDefinition<any, any, any, any>
|
|
382
383
|
| InfiniteQueryDefinition<any, any, any, any, any>,
|
|
383
384
|
) {
|
|
384
|
-
const queryAction: AnyQueryActionCreator
|
|
385
|
+
const queryAction: AnyQueryActionCreator =
|
|
385
386
|
(
|
|
386
387
|
arg,
|
|
387
388
|
{
|
|
@@ -390,7 +391,7 @@ You must add the middleware for RTK-Query to function correctly!`,
|
|
|
390
391
|
subscriptionOptions,
|
|
391
392
|
[forceQueryFnSymbol]: forceQueryFn,
|
|
392
393
|
...rest
|
|
393
|
-
} = {},
|
|
394
|
+
}: StartQueryActionCreatorOptions = {},
|
|
394
395
|
) =>
|
|
395
396
|
(dispatch, getState) => {
|
|
396
397
|
const queryCacheKey = serializeQueryArgs({
|
|
@@ -546,7 +547,10 @@ You must add the middleware for RTK-Query to function correctly!`,
|
|
|
546
547
|
function buildInitiateMutation(
|
|
547
548
|
endpointName: string,
|
|
548
549
|
): StartMutationActionCreator<any> {
|
|
549
|
-
return (
|
|
550
|
+
return (
|
|
551
|
+
arg,
|
|
552
|
+
{ track = true, fixedCacheKey }: StartMutationActionCreatorOptions = {},
|
|
553
|
+
) =>
|
|
550
554
|
(dispatch, getState) => {
|
|
551
555
|
const thunk = mutationThunk({
|
|
552
556
|
type: 'mutation',
|
|
@@ -129,7 +129,9 @@ export interface QueryCacheLifecycleApi<
|
|
|
129
129
|
BaseQuery extends BaseQueryFn,
|
|
130
130
|
ResultType,
|
|
131
131
|
ReducerPath extends string = string,
|
|
132
|
-
>
|
|
132
|
+
>
|
|
133
|
+
extends
|
|
134
|
+
QueryBaseLifecycleApi<QueryArg, BaseQuery, ResultType, ReducerPath>,
|
|
133
135
|
CacheLifecyclePromises<ResultType, BaseQueryMeta<BaseQuery>> {}
|
|
134
136
|
|
|
135
137
|
export type MutationCacheLifecycleApi<
|
|
@@ -118,7 +118,7 @@ export function buildMiddleware<
|
|
|
118
118
|
res = internalProbeResult
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
if (
|
|
121
|
+
if (mwApi.getState()[reducerPath]) {
|
|
122
122
|
// Only run these checks if the middleware is registered okay
|
|
123
123
|
|
|
124
124
|
// This looks for actions that aren't specific to the API slice
|
|
@@ -53,7 +53,10 @@ export const buildInvalidationByTagsHandler: InternalHandlerBuilder = ({
|
|
|
53
53
|
pendingRequestCount++
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
if (
|
|
56
|
+
if (
|
|
57
|
+
isQueryEnd(action) &&
|
|
58
|
+
!(queryThunk.rejected.match(action) && action.meta.condition)
|
|
59
|
+
) {
|
|
57
60
|
pendingRequestCount = Math.max(0, pendingRequestCount - 1)
|
|
58
61
|
}
|
|
59
62
|
|
|
@@ -102,8 +102,17 @@ export const buildPollingHandler: InternalHandlerBuilder = ({
|
|
|
102
102
|
nextPollTimestamp,
|
|
103
103
|
pollingInterval: lowestPollingInterval,
|
|
104
104
|
timeout: setTimeout(() => {
|
|
105
|
-
|
|
106
|
-
|
|
105
|
+
const currentState = api.getState()[reducerPath]
|
|
106
|
+
const currentQuerySubState = currentState.queries[queryCacheKey]
|
|
107
|
+
if (
|
|
108
|
+
!currentQuerySubState ||
|
|
109
|
+
currentQuerySubState.status === STATUS_UNINITIALIZED
|
|
110
|
+
) {
|
|
111
|
+
cleanupPollForKey(queryCacheKey)
|
|
112
|
+
return
|
|
113
|
+
}
|
|
114
|
+
if (currentState.config.focused || !skipPollingIfUnfocused) {
|
|
115
|
+
api.dispatch(refetchQuery(currentQuerySubState))
|
|
107
116
|
}
|
|
108
117
|
startNextPoll({ queryCacheKey }, api)
|
|
109
118
|
}, lowestPollingInterval),
|
|
@@ -167,7 +176,7 @@ export const buildPollingHandler: InternalHandlerBuilder = ({
|
|
|
167
176
|
let lowestPollingInterval = Number.POSITIVE_INFINITY
|
|
168
177
|
|
|
169
178
|
for (const entry of subscribers.values()) {
|
|
170
|
-
if (
|
|
179
|
+
if (entry.pollingInterval) {
|
|
171
180
|
lowestPollingInterval = Math.min(
|
|
172
181
|
entry.pollingInterval!,
|
|
173
182
|
lowestPollingInterval,
|