diff --git a/src/core/ObservableQuery.ts b/src/core/ObservableQuery.ts index 7db43eaeceb..49b8a791d3e 100644 --- a/src/core/ObservableQuery.ts +++ b/src/core/ObservableQuery.ts @@ -20,6 +20,7 @@ import { isNonEmptyArray } from '../utilities/common/arrays'; export type ApolloCurrentQueryResult = ApolloQueryResult & { error?: ApolloError; + partial?: boolean; }; export interface FetchMoreOptions< @@ -131,8 +132,12 @@ export class ObservableQuery< } public getCurrentResult(): ApolloCurrentQueryResult { - const { lastResult, lastError } = this; - const { fetchPolicy } = this.options; + const { + lastResult, + lastError, + options: { fetchPolicy }, + } = this; + const isNetworkFetchPolicy = fetchPolicy === 'network-only' || fetchPolicy === 'no-cache'; @@ -145,7 +150,7 @@ export class ObservableQuery< const result: ApolloCurrentQueryResult = { data: !lastError && lastResult && lastResult.data || void 0, - error: this.lastError, + error: lastError, loading: isNetworkRequestInFlight(networkStatus), networkStatus, stale: lastResult ? lastResult.stale : false, @@ -155,6 +160,9 @@ export class ObservableQuery< return result; } + const { data, partial } = this.queryManager.getCurrentQueryResult(this); + Object.assign(result, { data, partial }); + const queryStoreValue = this.queryManager.queryStore.get(this.queryId); if (queryStoreValue) { const { networkStatus } = queryStoreValue; @@ -176,11 +184,10 @@ export class ObservableQuery< // the original `ObservableQuery`. We'll update the observable query // variables here to match, so retrieving from the cache doesn't fail. if (queryStoreValue.variables) { - this.options.variables = { + this.variables = this.options.variables = { ...this.options.variables, ...(queryStoreValue.variables as TVariables), }; - this.variables = this.options.variables; } Object.assign(result, { @@ -193,7 +200,10 @@ export class ObservableQuery< } } - this.updateLastResult(result); + if (!partial) { + result.stale = false; + this.updateLastResult(result); + } return result; } diff --git a/src/core/QueryManager.ts b/src/core/QueryManager.ts index f6cb7a57702..391476e2dc2 100644 --- a/src/core/QueryManager.ts +++ b/src/core/QueryManager.ts @@ -1052,7 +1052,7 @@ export class QueryManager { this.queries.delete(queryId); } - private getCurrentQueryResult( + public getCurrentQueryResult( observableQuery: ObservableQuery, optimistic: boolean = true, ): { diff --git a/src/core/__tests__/ObservableQuery.ts b/src/core/__tests__/ObservableQuery.ts index 07dfe0dbfcd..5c8e3dbe9e1 100644 --- a/src/core/__tests__/ObservableQuery.ts +++ b/src/core/__tests__/ObservableQuery.ts @@ -1450,6 +1450,7 @@ describe('ObservableQuery', () => { loading: false, networkStatus: 7, stale: false, + partial: false, }); resolve(); }); @@ -1459,6 +1460,7 @@ describe('ObservableQuery', () => { data: undefined, networkStatus: 1, stale: false, + partial: true, }); setTimeout( @@ -1468,12 +1470,40 @@ describe('ObservableQuery', () => { data: undefined, networkStatus: 1, stale: false, + partial: true, }); }), 0, ); }); + itAsync('returns results from the store immediately', (resolve, reject) => { + const queryManager = mockQueryManager(reject, { + request: { query, variables }, + result: { data: dataOne }, + }); + + return queryManager.query({ query, variables }).then((result: any) => { + expect(stripSymbols(result)).toEqual({ + data: dataOne, + loading: false, + networkStatus: 7, + stale: false, + }); + const observable = queryManager.watchQuery({ + query, + variables, + }); + expect(stripSymbols(observable.getCurrentResult())).toEqual({ + data: dataOne, + loading: true, + networkStatus: NetworkStatus.loading, + stale: false, + partial: false, + }); + }).then(resolve, reject); + }); + itAsync('returns errors from the store immediately', (resolve, reject) => { const queryManager = mockQueryManager(reject, { request: { query, variables }, @@ -1563,7 +1593,7 @@ describe('ObservableQuery', () => { }).then(resolve, reject); }); - itAsync('returns partial data from the store', (resolve, reject) => { + itAsync('returns partial data from the store immediately', (resolve, reject) => { const superQuery = gql` query superQuery($id: ID!) { people_one(id: $id) { @@ -1600,10 +1630,11 @@ describe('ObservableQuery', () => { }); expect(observable.getCurrentResult()).toEqual({ - data: void 0, + data: dataOne, loading: true, networkStatus: 1, stale: false, + partial: true, }); // we can use this to trigger the query @@ -1618,28 +1649,19 @@ describe('ObservableQuery', () => { if (handleCount === 1) { expect(subResult).toEqual({ - data: void 0, + data: dataOne, loading: true, networkStatus: 1, stale: false, }); } else if (handleCount === 2) { - expect(subResult).toEqual({ - data: dataOne, - loading: false, - networkStatus: 7, - stale: false, - }); - - } else if (handleCount === 3) { expect(subResult).toEqual({ data: superDataOne, loading: false, networkStatus: 7, stale: false, }); - resolve(); } }); @@ -1670,6 +1692,7 @@ describe('ObservableQuery', () => { loading: true, networkStatus: 1, stale: false, + partial: false, }); subscribeAndCount(reject, observable, (handleCount, subResult) => { @@ -1679,29 +1702,21 @@ describe('ObservableQuery', () => { networkStatus, } = observable.getCurrentResult(); - expect(subResult).toEqual({ - data, - loading, - networkStatus, - stale: false, - }); - if (handleCount === 1) { - expect(stripSymbols(subResult)).toEqual({ - data: void 0, - loading: true, - networkStatus: NetworkStatus.loading, + expect(subResult).toEqual({ + data, + loading, + networkStatus, stale: false, + partial: false, }); - } else if (handleCount === 2) { expect(stripSymbols(subResult)).toEqual({ data: dataTwo, loading: false, - networkStatus: NetworkStatus.ready, + networkStatus: 7, stale: false, }); - resolve(); } }); @@ -1727,12 +1742,12 @@ describe('ObservableQuery', () => { variables, fetchPolicy: 'no-cache', }); - expect(stripSymbols(observable.getCurrentResult())).toEqual({ data: undefined, loading: true, networkStatus: 1, stale: false, + partial: false, }); subscribeAndCount(reject, observable, (handleCount, subResult) => { @@ -1748,6 +1763,7 @@ describe('ObservableQuery', () => { loading, networkStatus, stale: false, + partial: false, }); } else if (handleCount === 2) { expect(stripSymbols(subResult)).toEqual({ diff --git a/src/react/data/QueryData.ts b/src/react/data/QueryData.ts index fe56dc4f3ef..a1ed59e5c06 100644 --- a/src/react/data/QueryData.ts +++ b/src/react/data/QueryData.ts @@ -353,7 +353,7 @@ export class QueryData extends OperationData { } else { // Fetch the current result (if any) from the store. const currentResult = this.currentObservable.query!.getCurrentResult(); - const { loading, networkStatus, errors } = currentResult; + const { loading, partial, networkStatus, errors } = currentResult; let { error, data } = currentResult; // Until a set naming convention for networkError and graphQLErrors is @@ -390,6 +390,7 @@ export class QueryData extends OperationData { const { partialRefetch } = options; if ( partialRefetch && + partial && (!data || Object.keys(data).length === 0) && fetchPolicy !== 'cache-only' ) {