diff --git a/CHANGELOG.md b/CHANGELOG.md
index ac13e176201..9bce0637a95 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -124,6 +124,9 @@
- Missing `__typename` fields no longer cause the `InMemoryCache#diff` result to be marked `complete: false`, if those fields were added by `InMemoryCache#transformDocument` (which calls `addTypenameToDocument`).
[@benjamn](https://github.com/benjamn) in [#5787](https://github.com/apollographql/apollo-client/pull/5787)
+- Fixed an issue that allowed `@client @export` based queries to lead to extra unnecessary network requests being fired.
+ [@hwillson](https://github.com/hwillson) in [#5946](https://github.com/apollographql/apollo-client/pull/5946)
+
## Apollo Client 2.6.8
### Apollo Client (2.6.8)
diff --git a/package.json b/package.json
index 591e8db8d59..e8d37fa692b 100644
--- a/package.json
+++ b/package.json
@@ -48,7 +48,7 @@
{
"name": "apollo-client",
"path": "./dist/apollo-client.cjs.min.js",
- "maxSize": "24.1 kB"
+ "maxSize": "24.2 kB"
}
],
"peerDependencies": {
diff --git a/src/__tests__/local-state/export.ts b/src/__tests__/local-state/export.ts
index a9551dcbfa8..a771e8d424d 100644
--- a/src/__tests__/local-state/export.ts
+++ b/src/__tests__/local-state/export.ts
@@ -825,4 +825,83 @@ describe('@client @export tests', () => {
});
}
);
+
+ it(
+ 'should NOT attempt to refetch over the network if an @export variable ' +
+ 'has changed, the current fetch policy is cache-first, and the remote ' +
+ 'part of the query (that leverages the @export variable) can be fully ' +
+ 'found in the cache.',
+ done => {
+ const query = gql`
+ query currentAuthorPostCount($authorId: Int!) {
+ currentAuthorId @client @export(as: "authorId")
+ postCount(authorId: $authorId)
+ }
+ `;
+
+ const testAuthorId1 = 1;
+ const testPostCount1 = 100;
+
+ const testAuthorId2 = 2;
+ const testPostCount2 = 200;
+
+ let fetchCount = 0;
+ const link = new ApolloLink(() => {
+ fetchCount += 1;
+ return Observable.of({
+ data: {
+ postCount: testPostCount1
+ },
+ });
+ });
+
+ const cache = new InMemoryCache();
+ const client = new ApolloClient({
+ cache,
+ link,
+ resolvers: {},
+ });
+
+ client.writeQuery({
+ query: gql`{ currentAuthorId }`,
+ data: { currentAuthorId: testAuthorId1 }
+ });
+
+ let resultCount = 0;
+ const obs = client.watchQuery({ query, fetchPolicy: 'cache-first' });
+ obs.subscribe({
+ next(result) {
+ if (resultCount === 0) {
+ // The initial result is fetched over the network.
+ expect(fetchCount).toBe(1);
+ expect(result.data).toMatchObject({
+ currentAuthorId: testAuthorId1,
+ postCount: testPostCount1,
+ });
+
+ client.writeQuery({
+ query,
+ variables: { authorId: testAuthorId2 },
+ data: { postCount: testPostCount2 }
+ });
+ client.writeQuery({
+ query: gql`{ currentAuthorId }`,
+ data: { currentAuthorId: testAuthorId2 }
+ });
+ } else if (resultCount === 1) {
+ // The updated result should not have been fetched over the
+ // network, as it can be found in the cache.
+ expect(fetchCount).toBe(1);
+ expect(result.data).toMatchObject({
+ currentAuthorId: testAuthorId2,
+ postCount: testPostCount2,
+ });
+ done();
+ }
+
+ resultCount += 1;
+ },
+ });
+ }
+ );
});
diff --git a/src/core/ObservableQuery.ts b/src/core/ObservableQuery.ts
index 51e39d5388a..00a1e82069c 100644
--- a/src/core/ObservableQuery.ts
+++ b/src/core/ObservableQuery.ts
@@ -590,34 +590,43 @@ export class ObservableQuery<
iterateObserversSafely(this.observers, 'error', this.lastError = error);
};
+ const {
+ hasClientExports,
+ serverQuery
+ } = queryManager.transform(this.options.query);
+
queryManager.observeQuery(queryId, this.options, {
next: (result: ApolloQueryResult) => {
if (this.lastError || this.isDifferentFromLastResult(result)) {
const previousResult = this.updateLastResult(result);
+
const { query, variables, fetchPolicy } = this.options;
// Before calling `next` on each observer, we need to first see if
// the query is using `@client @export` directives, and update
// any variables that might have changed. If `@export` variables have
- // changed, and the query is calling against both local and remote
- // data, a refetch is needed to pull in new data, using the
- // updated `@export` variables.
- if (queryManager.transform(query).hasClientExports) {
+ // changed, and the query is requesting both local and remote
+ // data, `setVariables` is used as a network refetch might be
+ // needed to pull in new data, using the updated `@export` variables.
+ if (hasClientExports) {
queryManager.getLocalState().addExportedVariables(
query,
variables,
).then((variables: TVariables) => {
const previousVariables = this.variables;
- this.variables = this.options.variables = variables;
if (
!result.loading &&
previousResult &&
fetchPolicy !== 'cache-only' &&
- queryManager.transform(query).serverQuery &&
+ serverQuery &&
!equal(previousVariables, variables)
) {
- this.refetch();
+ this.setVariables(variables).then(updatedResult => {
+ this.variables = this.options.variables = variables;
+ iterateObserversSafely(this.observers, 'next', updatedResult)
+ });
} else {
+ this.variables = this.options.variables = variables;
iterateObserversSafely(this.observers, 'next', result);
}
});