Skip to content

Commit 762ee8a

Browse files
authored
allow for "shared component results" using the useMutation hook and fixedCacheKey (#1477)
1 parent 15a8ebd commit 762ee8a

File tree

8 files changed

+348
-49
lines changed

8 files changed

+348
-49
lines changed

packages/toolkit/src/query/core/apiState.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,15 @@ import type { Id, WithRequiredProp } from '../tsHelpers'
1212

1313
export type QueryCacheKey = string & { _type: 'queryCacheKey' }
1414
export type QuerySubstateIdentifier = { queryCacheKey: QueryCacheKey }
15-
export type MutationSubstateIdentifier = { requestId: string }
15+
export type MutationSubstateIdentifier =
16+
| {
17+
requestId: string
18+
fixedCacheKey?: string
19+
}
20+
| {
21+
requestId?: string
22+
fixedCacheKey: string
23+
}
1624

1725
export type RefetchConfigOptions = {
1826
refetchOnMountOrArgChange: boolean | number
@@ -175,6 +183,7 @@ export type QuerySubState<D extends BaseEndpointDefinition<any, any, any>> = Id<
175183
>
176184

177185
type BaseMutationSubState<D extends BaseEndpointDefinition<any, any, any>> = {
186+
requestId: string
178187
data?: ResultTypeFrom<D>
179188
error?:
180189
| SerializedError
@@ -200,6 +209,7 @@ export type MutationSubState<D extends BaseEndpointDefinition<any, any, any>> =
200209
status: QueryStatus.rejected
201210
} & WithRequiredProp<BaseMutationSubState<D>, 'error'>)
202211
| {
212+
requestId?: undefined
203213
status: QueryStatus.uninitialized
204214
data?: undefined
205215
error?: undefined

packages/toolkit/src/query/core/buildInitiate.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ type StartMutationActionCreator<
7979
* (defaults to `true`)
8080
*/
8181
track?: boolean
82+
fixedCacheKey?: string
8283
}
8384
) => ThunkAction<MutationActionCreatorResult<D>, any, any, AnyAction>
8485

@@ -113,6 +114,7 @@ export type MutationActionCreatorResult<
113114
* Whether the mutation is being tracked in the store.
114115
*/
115116
track?: boolean
117+
fixedCacheKey?: string
116118
}
117119
/**
118120
* A unique string generated for the request sequence
@@ -287,12 +289,13 @@ Features like automatic cache collection, automatic refetching etc. will not be
287289
endpointName: string,
288290
definition: MutationDefinition<any, any, any, any>
289291
): StartMutationActionCreator<any> {
290-
return (arg, { track = true } = {}) =>
292+
return (arg, { track = true, fixedCacheKey } = {}) =>
291293
(dispatch, getState) => {
292294
const thunk = mutationThunk({
293295
endpointName,
294296
originalArgs: arg,
295297
track,
298+
fixedCacheKey,
296299
})
297300
const thunkResult = dispatch(thunk)
298301
middlewareWarning(getState)
@@ -303,7 +306,7 @@ Features like automatic cache collection, automatic refetching etc. will not be
303306
.catch((error) => ({ error }))
304307

305308
const reset = () => {
306-
if (track) dispatch(removeMutationResult({ requestId }))
309+
dispatch(removeMutationResult({ requestId, fixedCacheKey }))
307310
}
308311

309312
return Object.assign(returnValuePromise, {

packages/toolkit/src/query/core/buildMiddleware/cacheLifecycle.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import type {
88
MutationResultSelectorResult,
99
QueryResultSelectorResult,
1010
} from '../buildSelectors'
11+
import { getMutationCacheKey } from '../buildSlice'
1112
import type { PatchCollection, Recipe } from '../buildThunks'
1213
import type {
1314
PromiseWithKnownReason,
@@ -258,7 +259,7 @@ export const build: SubMiddlewareBuilder = ({
258259
if (api.internalActions.removeQueryResult.match(action))
259260
return action.payload.queryCacheKey
260261
if (api.internalActions.removeMutationResult.match(action))
261-
return action.payload.requestId
262+
return getMutationCacheKey(action.payload)
262263
return ''
263264
}
264265

packages/toolkit/src/query/core/buildSelectors.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import type {
1515
ReducerPathFrom,
1616
} from '../endpointDefinitions'
1717
import type { InternalSerializeQueryArgs } from '../defaultSerializeQueryArgs'
18+
import { getMutationCacheKey } from './buildSlice'
1819

1920
export type SkipToken = typeof skipToken
2021
/**
@@ -88,7 +89,10 @@ type MutationResultSelectorFactory<
8889
Definition extends MutationDefinition<any, any, any, any>,
8990
RootState
9091
> = (
91-
requestId: string | SkipToken
92+
requestId:
93+
| string
94+
| { requestId: string | undefined; fixedCacheKey: string | undefined }
95+
| SkipToken
9296
) => (state: RootState) => MutationResultSelectorResult<Definition>
9397

9498
export type MutationResultSelectorResult<
@@ -172,7 +176,13 @@ export function buildSelectors<
172176
any,
173177
RootState
174178
> {
175-
return (mutationId) => {
179+
return (id) => {
180+
let mutationId: string | typeof skipToken
181+
if (typeof id === 'object') {
182+
mutationId = getMutationCacheKey(id) ?? skipToken
183+
} else {
184+
mutationId = id
185+
}
176186
const selectMutationSubstate = createSelector(
177187
selectInternalState,
178188
(internalState) =>

packages/toolkit/src/query/core/buildSlice.ts

Lines changed: 50 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,7 @@ import type {
2828
ConfigState,
2929
} from './apiState'
3030
import { QueryStatus } from './apiState'
31-
import type {
32-
MutationThunk,
33-
MutationThunkArg,
34-
QueryThunk,
35-
QueryThunkArg,
36-
ThunkResult,
37-
} from './buildThunks'
31+
import type { MutationThunk, QueryThunk } from './buildThunks'
3832
import { calculateProvidedByThunk } from './buildThunks'
3933
import type {
4034
AssertTagTypes,
@@ -61,12 +55,33 @@ function updateQuerySubstateIfExists(
6155
}
6256
}
6357

58+
export function getMutationCacheKey(
59+
id:
60+
| MutationSubstateIdentifier
61+
| { requestId: string; arg: { fixedCacheKey?: string | undefined } }
62+
): string
63+
export function getMutationCacheKey(id: {
64+
fixedCacheKey?: string
65+
requestId?: string
66+
}): string | undefined
67+
68+
export function getMutationCacheKey(
69+
id:
70+
| { fixedCacheKey?: string; requestId?: string }
71+
| MutationSubstateIdentifier
72+
| { requestId: string; arg: { fixedCacheKey?: string | undefined } }
73+
): string | undefined {
74+
return ('arg' in id ? id.arg.fixedCacheKey : id.fixedCacheKey) ?? id.requestId
75+
}
76+
6477
function updateMutationSubstateIfExists(
6578
state: MutationState<any>,
66-
{ requestId }: MutationSubstateIdentifier,
79+
id:
80+
| MutationSubstateIdentifier
81+
| { requestId: string; arg: { fixedCacheKey?: string | undefined } },
6782
update: (substate: MutationSubState<any>) => void
6883
) {
69-
const substate = state[requestId]
84+
const substate = state[getMutationCacheKey(id)]
7085
if (substate) {
7186
update(substate)
7287
}
@@ -174,50 +189,50 @@ export function buildSlice({
174189
reducers: {
175190
removeMutationResult(
176191
draft,
177-
action: PayloadAction<MutationSubstateIdentifier>
192+
{ payload }: PayloadAction<MutationSubstateIdentifier>
178193
) {
179-
if (action.payload.requestId in draft) {
180-
delete draft[action.payload.requestId]
194+
const cacheKey = getMutationCacheKey(payload)
195+
if (cacheKey in draft) {
196+
delete draft[cacheKey]
181197
}
182198
},
183199
},
184200
extraReducers(builder) {
185201
builder
186202
.addCase(
187203
mutationThunk.pending,
188-
(draft, { meta: { arg, requestId, startedTimeStamp } }) => {
204+
(draft, { meta, meta: { requestId, arg, startedTimeStamp } }) => {
189205
if (!arg.track) return
190206

191-
draft[requestId] = {
207+
draft[getMutationCacheKey(meta)] = {
208+
requestId,
192209
status: QueryStatus.pending,
193210
endpointName: arg.endpointName,
194211
startedTimeStamp,
195212
}
196213
}
197214
)
198-
.addCase(
199-
mutationThunk.fulfilled,
200-
(draft, { payload, meta, meta: { requestId } }) => {
201-
if (!meta.arg.track) return
215+
.addCase(mutationThunk.fulfilled, (draft, { payload, meta }) => {
216+
if (!meta.arg.track) return
202217

203-
updateMutationSubstateIfExists(draft, { requestId }, (substate) => {
204-
substate.status = QueryStatus.fulfilled
205-
substate.data = payload
206-
substate.fulfilledTimeStamp = meta.fulfilledTimeStamp
207-
})
208-
}
209-
)
210-
.addCase(
211-
mutationThunk.rejected,
212-
(draft, { payload, error, meta: { requestId, arg } }) => {
213-
if (!arg.track) return
218+
updateMutationSubstateIfExists(draft, meta, (substate) => {
219+
if (substate.requestId !== meta.requestId) return
214220

215-
updateMutationSubstateIfExists(draft, { requestId }, (substate) => {
216-
substate.status = QueryStatus.rejected
217-
substate.error = (payload ?? error) as any
218-
})
219-
}
220-
)
221+
substate.status = QueryStatus.fulfilled
222+
substate.data = payload
223+
substate.fulfilledTimeStamp = meta.fulfilledTimeStamp
224+
})
225+
})
226+
.addCase(mutationThunk.rejected, (draft, { payload, error, meta }) => {
227+
if (!meta.arg.track) return
228+
229+
updateMutationSubstateIfExists(draft, meta, (substate) => {
230+
if (substate.requestId !== meta.requestId) return
231+
232+
substate.status = QueryStatus.rejected
233+
substate.error = (payload ?? error) as any
234+
})
235+
})
221236
},
222237
})
223238

packages/toolkit/src/query/core/buildThunks.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ export interface MutationThunkArg {
111111
originalArgs: unknown
112112
endpointName: string
113113
track?: boolean
114+
fixedCacheKey?: string
114115
}
115116

116117
export type ThunkResult = unknown

packages/toolkit/src/query/react/buildHooks.ts

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,7 @@ export type UseMutationStateOptions<
401401
R extends Record<string, any>
402402
> = {
403403
selectFromResult?: MutationStateSelector<R, D>
404+
fixedCacheKey?: string
404405
}
405406

406407
export type UseMutationStateResult<
@@ -781,35 +782,60 @@ export function buildHooks<Definitions extends EndpointDefinitions>({
781782
}
782783

783784
function buildMutationHook(name: string): UseMutation<any> {
784-
return ({ selectFromResult = defaultMutationStateSelector } = {}) => {
785+
return ({
786+
selectFromResult = defaultMutationStateSelector,
787+
fixedCacheKey,
788+
} = {}) => {
785789
const { select, initiate } = api.endpoints[name] as ApiEndpointMutation<
786790
MutationDefinition<any, any, any, any, any>,
787791
Definitions
788792
>
789793
const dispatch = useDispatch<ThunkDispatch<any, any, AnyAction>>()
790794
const [promise, setPromise] = useState<MutationActionCreatorResult<any>>()
791795

792-
useEffect(() => () => promise?.reset(), [promise])
796+
useEffect(
797+
() => () => {
798+
if (!promise?.arg.fixedCacheKey) {
799+
promise?.reset()
800+
}
801+
},
802+
[promise]
803+
)
793804

794805
const triggerMutation = useCallback(
795806
function (arg) {
796-
const promise = dispatch(initiate(arg))
807+
const promise = dispatch(initiate(arg, { fixedCacheKey }))
797808
setPromise(promise)
798809
return promise
799810
},
800-
[dispatch, initiate]
811+
[dispatch, initiate, fixedCacheKey]
801812
)
802813

803814
const { requestId } = promise || {}
804815
const mutationSelector = useMemo(
805816
() =>
806-
createSelector([select(requestId || skipToken)], selectFromResult),
807-
[select, requestId, selectFromResult]
817+
createSelector(
818+
[select({ fixedCacheKey, requestId: promise?.requestId })],
819+
selectFromResult
820+
),
821+
[select, promise, selectFromResult, fixedCacheKey]
808822
)
809823

810824
const currentState = useSelector(mutationSelector, shallowEqual)
811-
const originalArgs = promise?.arg.originalArgs
812-
const reset = useCallback(() => setPromise(undefined), [])
825+
const originalArgs =
826+
fixedCacheKey == null ? promise?.arg.originalArgs : undefined
827+
const reset = useCallback(() => {
828+
if (promise) {
829+
setPromise(undefined)
830+
} else if (fixedCacheKey) {
831+
dispatch(
832+
api.internalActions.removeMutationResult({
833+
requestId,
834+
fixedCacheKey,
835+
})
836+
)
837+
}
838+
}, [dispatch, fixedCacheKey, promise, requestId])
813839
const finalState = useMemo(
814840
() => ({ ...currentState, originalArgs, reset }),
815841
[currentState, originalArgs, reset]

0 commit comments

Comments
 (0)