From a88be56769766c733ca57f04d6d98723cde4a91e Mon Sep 17 00:00:00 2001 From: Beverly Boydston Date: Tue, 18 Jan 2022 16:40:12 -0500 Subject: [PATCH 1/4] feat(query): Adds subscribe option to prefetch, automatically unsubscribes if not present [#1283] --- .../toolkit/src/query/core/buildThunks.ts | 38 ++++++++++++++++--- packages/toolkit/src/query/core/module.ts | 7 +++- 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/packages/toolkit/src/query/core/buildThunks.ts b/packages/toolkit/src/query/core/buildThunks.ts index 58496877aa..64614da8f2 100644 --- a/packages/toolkit/src/query/core/buildThunks.ts +++ b/packages/toolkit/src/query/core/buildThunks.ts @@ -8,7 +8,10 @@ import type { import { BaseQueryArg } from '../baseQueryTypes' import type { RootState, QueryKeys, QuerySubstateIdentifier } from './apiState' import { QueryStatus, CombinedState } from './apiState' -import type { StartQueryActionCreatorOptions } from './buildInitiate' +import type { + QueryActionCreatorResult, + StartQueryActionCreatorOptions, +} from './buildInitiate' import type { AssertTagTypes, EndpointDefinition, @@ -430,6 +433,17 @@ In the case of an unhandled error, no tags will be "provided" or "invalidated".` const hasMaxAge = ( options: any ): options is { ifOlderThan: false | number } => 'ifOlderThan' in options + const hasSubscribe = (options: any): options is { subscribe: boolean } => + 'subscribe' in options + const handlePrefetchSubscription = ( + result: QueryActionCreatorResult, + subscribe: boolean + ): void | QueryActionCreatorResult => { + if (subscribe) { + return result + } + result.unwrap().finally(result.unsubscribe) + } const prefetch = >( @@ -440,6 +454,8 @@ In the case of an unhandled error, no tags will be "provided" or "invalidated".` (dispatch: ThunkDispatch, getState: () => any) => { const force = hasTheForce(options) && options.force const maxAge = hasMaxAge(options) && options.ifOlderThan + const handleUnsubscribeDownstream = + hasSubscribe(options) && options.subscribe const queryAction = (force: boolean = true) => (api.endpoints[endpointName] as ApiEndpointQuery).initiate( @@ -451,22 +467,34 @@ In the case of an unhandled error, no tags will be "provided" or "invalidated".` ).select(arg)(getState()) if (force) { - dispatch(queryAction()) + handlePrefetchSubscription( + dispatch(queryAction()), + handleUnsubscribeDownstream + ) } else if (maxAge) { const lastFulfilledTs = latestStateValue?.fulfilledTimeStamp if (!lastFulfilledTs) { - dispatch(queryAction()) + handlePrefetchSubscription( + dispatch(queryAction()), + handleUnsubscribeDownstream + ) return } const shouldRetrigger = (Number(new Date()) - Number(new Date(lastFulfilledTs))) / 1000 >= maxAge if (shouldRetrigger) { - dispatch(queryAction()) + handlePrefetchSubscription( + dispatch(queryAction()), + handleUnsubscribeDownstream + ) } } else { // If prefetching with no options, just let it try - dispatch(queryAction(false)) + handlePrefetchSubscription( + dispatch(queryAction(false)), + handleUnsubscribeDownstream + ) } } diff --git a/packages/toolkit/src/query/core/module.ts b/packages/toolkit/src/query/core/module.ts index 91f048a322..ecb73729b4 100644 --- a/packages/toolkit/src/query/core/module.ts +++ b/packages/toolkit/src/query/core/module.ts @@ -49,16 +49,21 @@ import { enablePatches } from 'immer' /** * `ifOlderThan` - (default: `false` | `number`) - _number is value in seconds_ * - If specified, it will only run the query if the difference between `new Date()` and the last `fulfilledTimeStamp` is greater than the given value + * `subscribe` - (default: `false` | `boolean`) + * - If `true`, `prefetch` will return an interface with `unsubscribe` * * @overloadSummary * `force` * - If `force: true`, it will ignore the `ifOlderThan` value if it is set and the query will be run even if it exists in the cache. */ -export type PrefetchOptions = +export type PrefetchOptions = ( | { ifOlderThan?: false | number } | { force?: boolean } +) & { + subscribe?: boolean +} export const coreModuleName = /* @__PURE__ */ Symbol() export type CoreModule = From 781a1cb100011e0b7b2e78a7ccca5ac966b0e141 Mon Sep 17 00:00:00 2001 From: Beverly Boydston Date: Tue, 18 Jan 2022 16:53:04 -0500 Subject: [PATCH 2/4] fix(query): Fixes code not magically returning values --- packages/toolkit/src/query/core/buildThunks.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/toolkit/src/query/core/buildThunks.ts b/packages/toolkit/src/query/core/buildThunks.ts index 64614da8f2..a9e34c9e00 100644 --- a/packages/toolkit/src/query/core/buildThunks.ts +++ b/packages/toolkit/src/query/core/buildThunks.ts @@ -467,31 +467,30 @@ In the case of an unhandled error, no tags will be "provided" or "invalidated".` ).select(arg)(getState()) if (force) { - handlePrefetchSubscription( + return handlePrefetchSubscription( dispatch(queryAction()), handleUnsubscribeDownstream ) } else if (maxAge) { const lastFulfilledTs = latestStateValue?.fulfilledTimeStamp if (!lastFulfilledTs) { - handlePrefetchSubscription( + return handlePrefetchSubscription( dispatch(queryAction()), handleUnsubscribeDownstream ) - return } const shouldRetrigger = (Number(new Date()) - Number(new Date(lastFulfilledTs))) / 1000 >= maxAge if (shouldRetrigger) { - handlePrefetchSubscription( + return handlePrefetchSubscription( dispatch(queryAction()), handleUnsubscribeDownstream ) } } else { // If prefetching with no options, just let it try - handlePrefetchSubscription( + return handlePrefetchSubscription( dispatch(queryAction(false)), handleUnsubscribeDownstream ) From c198c1c9ccc60ff55358675a005a896c016e2daa Mon Sep 17 00:00:00 2001 From: Beverly Boydston Date: Tue, 18 Jan 2022 18:12:15 -0500 Subject: [PATCH 3/4] task(query): Reverts prefetch resolving query result --- .../toolkit/src/query/core/buildThunks.ts | 34 ++++--------------- packages/toolkit/src/query/core/module.ts | 7 +--- 2 files changed, 7 insertions(+), 34 deletions(-) diff --git a/packages/toolkit/src/query/core/buildThunks.ts b/packages/toolkit/src/query/core/buildThunks.ts index a9e34c9e00..428dc17826 100644 --- a/packages/toolkit/src/query/core/buildThunks.ts +++ b/packages/toolkit/src/query/core/buildThunks.ts @@ -433,17 +433,8 @@ In the case of an unhandled error, no tags will be "provided" or "invalidated".` const hasMaxAge = ( options: any ): options is { ifOlderThan: false | number } => 'ifOlderThan' in options - const hasSubscribe = (options: any): options is { subscribe: boolean } => - 'subscribe' in options - const handlePrefetchSubscription = ( - result: QueryActionCreatorResult, - subscribe: boolean - ): void | QueryActionCreatorResult => { - if (subscribe) { - return result - } + const handlePrefetchSubscription = (result: QueryActionCreatorResult) => result.unwrap().finally(result.unsubscribe) - } const prefetch = >( @@ -454,8 +445,6 @@ In the case of an unhandled error, no tags will be "provided" or "invalidated".` (dispatch: ThunkDispatch, getState: () => any) => { const force = hasTheForce(options) && options.force const maxAge = hasMaxAge(options) && options.ifOlderThan - const handleUnsubscribeDownstream = - hasSubscribe(options) && options.subscribe const queryAction = (force: boolean = true) => (api.endpoints[endpointName] as ApiEndpointQuery).initiate( @@ -467,33 +456,22 @@ In the case of an unhandled error, no tags will be "provided" or "invalidated".` ).select(arg)(getState()) if (force) { - return handlePrefetchSubscription( - dispatch(queryAction()), - handleUnsubscribeDownstream - ) + handlePrefetchSubscription(dispatch(queryAction())) } else if (maxAge) { const lastFulfilledTs = latestStateValue?.fulfilledTimeStamp if (!lastFulfilledTs) { - return handlePrefetchSubscription( - dispatch(queryAction()), - handleUnsubscribeDownstream - ) + handlePrefetchSubscription(dispatch(queryAction())) + return } const shouldRetrigger = (Number(new Date()) - Number(new Date(lastFulfilledTs))) / 1000 >= maxAge if (shouldRetrigger) { - return handlePrefetchSubscription( - dispatch(queryAction()), - handleUnsubscribeDownstream - ) + handlePrefetchSubscription(dispatch(queryAction())) } } else { // If prefetching with no options, just let it try - return handlePrefetchSubscription( - dispatch(queryAction(false)), - handleUnsubscribeDownstream - ) + handlePrefetchSubscription(dispatch(queryAction(false))) } } diff --git a/packages/toolkit/src/query/core/module.ts b/packages/toolkit/src/query/core/module.ts index ecb73729b4..91f048a322 100644 --- a/packages/toolkit/src/query/core/module.ts +++ b/packages/toolkit/src/query/core/module.ts @@ -49,21 +49,16 @@ import { enablePatches } from 'immer' /** * `ifOlderThan` - (default: `false` | `number`) - _number is value in seconds_ * - If specified, it will only run the query if the difference between `new Date()` and the last `fulfilledTimeStamp` is greater than the given value - * `subscribe` - (default: `false` | `boolean`) - * - If `true`, `prefetch` will return an interface with `unsubscribe` * * @overloadSummary * `force` * - If `force: true`, it will ignore the `ifOlderThan` value if it is set and the query will be run even if it exists in the cache. */ -export type PrefetchOptions = ( +export type PrefetchOptions = | { ifOlderThan?: false | number } | { force?: boolean } -) & { - subscribe?: boolean -} export const coreModuleName = /* @__PURE__ */ Symbol() export type CoreModule = From 81d929a880020c922e34af4435bf47d64b5b11e6 Mon Sep 17 00:00:00 2001 From: Beverly Boydston Date: Tue, 18 Jan 2022 18:57:26 -0500 Subject: [PATCH 4/4] task(query): Revert making subscription cleanup required --- .../toolkit/src/query/core/buildThunks.ts | 21 +++++++++++++------ packages/toolkit/src/query/core/module.ts | 5 ++++- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/packages/toolkit/src/query/core/buildThunks.ts b/packages/toolkit/src/query/core/buildThunks.ts index 428dc17826..f55e56549a 100644 --- a/packages/toolkit/src/query/core/buildThunks.ts +++ b/packages/toolkit/src/query/core/buildThunks.ts @@ -433,8 +433,8 @@ In the case of an unhandled error, no tags will be "provided" or "invalidated".` const hasMaxAge = ( options: any ): options is { ifOlderThan: false | number } => 'ifOlderThan' in options - const handlePrefetchSubscription = (result: QueryActionCreatorResult) => - result.unwrap().finally(result.unsubscribe) + const hasCleanup = (options: any): options is { cleanup?: boolean } => + 'cleanup' in options const prefetch = >( @@ -445,33 +445,42 @@ In the case of an unhandled error, no tags will be "provided" or "invalidated".` (dispatch: ThunkDispatch, getState: () => any) => { const force = hasTheForce(options) && options.force const maxAge = hasMaxAge(options) && options.ifOlderThan + const cleanupSubscription = hasCleanup(options) && options.cleanup const queryAction = (force: boolean = true) => (api.endpoints[endpointName] as ApiEndpointQuery).initiate( arg, { forceRefetch: force } ) + const queryCleanup = ( + result: QueryActionCreatorResult, + doCleanup: boolean = false + ) => { + if (doCleanup) { + result.unwrap().finally(result.unsubscribe) + } + } const latestStateValue = ( api.endpoints[endpointName] as ApiEndpointQuery ).select(arg)(getState()) if (force) { - handlePrefetchSubscription(dispatch(queryAction())) + queryCleanup(dispatch(queryAction()), cleanupSubscription) } else if (maxAge) { const lastFulfilledTs = latestStateValue?.fulfilledTimeStamp if (!lastFulfilledTs) { - handlePrefetchSubscription(dispatch(queryAction())) + queryCleanup(dispatch(queryAction()), cleanupSubscription) return } const shouldRetrigger = (Number(new Date()) - Number(new Date(lastFulfilledTs))) / 1000 >= maxAge if (shouldRetrigger) { - handlePrefetchSubscription(dispatch(queryAction())) + queryCleanup(dispatch(queryAction()), cleanupSubscription) } } else { // If prefetching with no options, just let it try - handlePrefetchSubscription(dispatch(queryAction(false))) + queryCleanup(dispatch(queryAction(false)), cleanupSubscription) } } diff --git a/packages/toolkit/src/query/core/module.ts b/packages/toolkit/src/query/core/module.ts index 91f048a322..42b58efad5 100644 --- a/packages/toolkit/src/query/core/module.ts +++ b/packages/toolkit/src/query/core/module.ts @@ -54,11 +54,14 @@ import { enablePatches } from 'immer' * `force` * - If `force: true`, it will ignore the `ifOlderThan` value if it is set and the query will be run even if it exists in the cache. */ -export type PrefetchOptions = +export type PrefetchOptions = ( | { ifOlderThan?: false | number } | { force?: boolean } +) & { + cleanup?: boolean +} export const coreModuleName = /* @__PURE__ */ Symbol() export type CoreModule =