diff --git a/src/core/ObservableQuery.ts b/src/core/ObservableQuery.ts index 76b31b12d51..5d9c285906f 100644 --- a/src/core/ObservableQuery.ts +++ b/src/core/ObservableQuery.ts @@ -224,6 +224,16 @@ export class ObservableQuery< * the previous values of those variables will be used. */ public refetch(variables?: Partial): Promise> { + return this.refresh(variables, NetworkStatus.refetch); + } + + // Forces a network query, like fetchMore and refetch, but overwrites + // existing cache fields with incoming data, rather than merging field + // values. Can be useful for restarting paginated fields with fresh data. + public refresh( + variables?: Partial, + networkStatus = NetworkStatus.refresh, + ): Promise> { const reobserveOptions: Partial> = { // Always disable polling for refetches. pollInterval: 0, @@ -250,9 +260,13 @@ export class ObservableQuery< this.queryInfo.resetLastWrite(); - return this.newReobserver(false).reobserve( + return ( + networkStatus === NetworkStatus.refresh + ? this.getReobserver() + : this.newReobserver(false) + ).reobserve( reobserveOptions, - NetworkStatus.refetch, + networkStatus, ); } diff --git a/src/core/QueryManager.ts b/src/core/QueryManager.ts index 4451e9cd116..6cd9e182b69 100644 --- a/src/core/QueryManager.ts +++ b/src/core/QueryManager.ts @@ -886,7 +886,7 @@ export class QueryManager { options: WatchQueryOptions, // The initial networkStatus for this fetch, most often // NetworkStatus.loading, but also possibly fetchMore, poll, refetch, - // or setVariables. + // refresh, or setVariables. networkStatus = NetworkStatus.loading, ): Concast> { const query = this.transform(options.query).document; @@ -1039,7 +1039,7 @@ export class QueryManager { options: WatchQueryOptions, // The initial networkStatus for this fetch, most often // NetworkStatus.loading, but also possibly fetchMore, poll, refetch, - // or setVariables. + // refresh, or setVariables. networkStatus: NetworkStatus, ): ConcastSourcesIterable> { const { @@ -1097,7 +1097,7 @@ export class QueryManager { const cacheWriteBehavior = fetchPolicy === "no-cache" ? CacheWriteBehavior.FORBID : - networkStatus === NetworkStatus.refetch ? CacheWriteBehavior.OVERWRITE : + networkStatus === NetworkStatus.refresh ? CacheWriteBehavior.OVERWRITE : CacheWriteBehavior.MERGE; const resultsFromLink = () => diff --git a/src/core/networkStatus.ts b/src/core/networkStatus.ts index 08915e3a702..9c9c736e675 100644 --- a/src/core/networkStatus.ts +++ b/src/core/networkStatus.ts @@ -27,6 +27,18 @@ export enum NetworkStatus { */ refetch = 4, + /** + * Similar to NetworkStatus.refetch, but existing cache fields will be + * overwritten by the incoming network data, rather than merging. Useful + * for restarting a paginated field with fresh initial data. + * + * This enum value uses the string "refetch" rather than a number, + * because it was added after the other values, and we did not want to + * change the existing numbers. Any other values added in the future + * should also use strings rather than numbers. + */ + refresh = "refresh", + /** * Indicates that a polling query is currently in flight. So for example if you are polling a * query every 10 seconds then the network status will switch to `poll` every 10 seconds whenever @@ -52,5 +64,6 @@ export enum NetworkStatus { export function isNetworkRequestInFlight( networkStatus?: NetworkStatus, ): boolean { - return networkStatus ? networkStatus < 7 : false; + return networkStatus === "refresh" || + (typeof networkStatus === "number" && networkStatus < 7); } diff --git a/src/react/components/__tests__/client/__snapshots__/Query.test.tsx.snap b/src/react/components/__tests__/client/__snapshots__/Query.test.tsx.snap index 8e19a2575ff..1a05e8fa09f 100644 --- a/src/react/components/__tests__/client/__snapshots__/Query.test.tsx.snap +++ b/src/react/components/__tests__/client/__snapshots__/Query.test.tsx.snap @@ -28,6 +28,7 @@ Object { "networkStatus": 1, "previousData": undefined, "refetch": [Function], + "refresh": [Function], "startPolling": [Function], "stopPolling": [Function], "subscribeToMore": [Function], diff --git a/src/react/data/QueryData.ts b/src/react/data/QueryData.ts index c14d5b671f9..f16179123c4 100644 --- a/src/react/data/QueryData.ts +++ b/src/react/data/QueryData.ts @@ -495,6 +495,9 @@ export class QueryData extends OperationData< private obsRefetch = (variables?: Partial) => this.currentObservable?.refetch(variables); + private obsRefresh = (variables?: Partial) => + this.currentObservable?.refresh(variables); + private obsFetchMore = ( fetchMoreOptions: FetchMoreQueryOptions & FetchMoreOptions @@ -530,6 +533,7 @@ export class QueryData extends OperationData< return { variables: this.currentObservable?.variables, refetch: this.obsRefetch, + refresh: this.obsRefresh, fetchMore: this.obsFetchMore, updateQuery: this.obsUpdateQuery, startPolling: this.obsStartPolling, diff --git a/src/react/hoc/types.ts b/src/react/hoc/types.ts index dddfe8d6f1f..a9abe3e9455 100644 --- a/src/react/hoc/types.ts +++ b/src/react/hoc/types.ts @@ -1,4 +1,4 @@ -import { ApolloClient } from '../../core'; +import { ApolloClient, ObservableQuery } from '../../core'; import { ApolloError } from '../../errors'; import { ApolloQueryResult, @@ -28,6 +28,7 @@ export interface QueryControls< FetchMoreOptions ) => Promise>; refetch: (variables?: TGraphQLVariables) => Promise>; + refresh: ObservableQuery["refresh"]; startPolling: (pollInterval: number) => void; stopPolling: () => void; subscribeToMore: (options: SubscribeToMoreOptions) => () => void; diff --git a/src/react/types/types.ts b/src/react/types/types.ts index e08054bd9d9..53878eeeb20 100644 --- a/src/react/types/types.ts +++ b/src/react/types/types.ts @@ -61,6 +61,7 @@ export type ObservableQueryFields = Pick< | 'subscribeToMore' | 'updateQuery' | 'refetch' + | 'refresh' | 'variables' > & { fetchMore: ((