Skip to content

Commit 187828a

Browse files
committed
Fixed issue causing inactive queries to be executed when clearing or resetting the store
Closes #11914
1 parent 116723d commit 187828a

File tree

3 files changed

+80
-31
lines changed

3 files changed

+80
-31
lines changed

Diff for: .changeset/plenty-wolves-fetch.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@apollo/client": patch
3+
---
4+
5+
Fixed issue causing inactive queries to be executed when clearing or resetting the store

Diff for: src/core/QueryInfo.ts

+20-14
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
1-
import type { DocumentNode, GraphQLError } from "graphql";
21
import { equal } from "@wry/equality";
2+
import type { DocumentNode, GraphQLError } from "graphql";
33

4-
import type { Cache, ApolloCache } from "../cache/index.js";
5-
import { DeepMerger } from "../utilities/index.js";
6-
import { mergeIncrementalData } from "../utilities/index.js";
7-
import type { WatchQueryOptions, ErrorPolicy } from "./watchQueryOptions.js";
8-
import type { ObservableQuery } from "./ObservableQuery.js";
9-
import { reobserveCacheFirst } from "./ObservableQuery.js";
10-
import type { QueryListener } from "./types.js";
4+
import type { ApolloCache, Cache } from "../cache/index.js";
5+
import type { ApolloError } from "../errors/index.js";
116
import type { FetchResult } from "../link/core/index.js";
127
import {
13-
isNonEmptyArray,
14-
graphQLResultHasError,
8+
DeepMerger,
159
canUseWeakMap,
10+
graphQLResultHasError,
11+
isNonEmptyArray,
12+
mergeIncrementalData,
1613
} from "../utilities/index.js";
17-
import { NetworkStatus, isNetworkRequestInFlight } from "./networkStatus.js";
18-
import type { ApolloError } from "../errors/index.js";
14+
import type { ObservableQuery } from "./ObservableQuery.js";
15+
import { reobserveCacheFirst } from "./ObservableQuery.js";
1916
import type { QueryManager } from "./QueryManager.js";
17+
import { NetworkStatus, isNetworkRequestInFlight } from "./networkStatus.js";
18+
import type { QueryListener } from "./types.js";
19+
import type { ErrorPolicy, WatchQueryOptions } from "./watchQueryOptions.js";
2020

2121
export type QueryStoreValue = Pick<
2222
QueryInfo,
@@ -287,11 +287,17 @@ export class QueryInfo {
287287
}
288288

289289
private shouldNotify() {
290-
if (!this.dirty || !this.listeners.size) {
290+
if (
291+
!this.dirty ||
292+
!this.listeners.size ||
293+
// It's possible that the query is no longer being watched, but the
294+
// ObservableQuery is still active/pending cleanup. In this case, we should not notify.
295+
!this.observableQuery?.hasObservers()
296+
) {
291297
return false;
292298
}
293299

294-
if (isNetworkRequestInFlight(this.networkStatus) && this.observableQuery) {
300+
if (isNetworkRequestInFlight(this.networkStatus)) {
295301
const { fetchPolicy } = this.observableQuery.options;
296302
if (fetchPolicy !== "cache-only" && fetchPolicy !== "cache-and-network") {
297303
return false;

Diff for: src/core/__tests__/QueryManager/index.ts

+55-17
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,53 @@
11
// externals
2+
import { DocumentNode, GraphQLError } from "graphql";
3+
import gql from "graphql-tag";
4+
import { assign } from "lodash";
25
import { from } from "rxjs";
36
import { map } from "rxjs/operators";
4-
import { assign } from "lodash";
5-
import gql from "graphql-tag";
6-
import { DocumentNode, GraphQLError } from "graphql";
77
import { setVerbosity } from "ts-invariant";
88

9-
import {
10-
Observable,
11-
Observer,
12-
} from "../../../utilities/observables/Observable";
13-
import { ApolloLink, GraphQLRequest, FetchResult } from "../../../link/core";
149
import { InMemoryCache, InMemoryCacheConfig } from "../../../cache";
1510
import {
1611
ApolloReducerConfig,
1712
NormalizedCacheObject,
1813
} from "../../../cache/inmemory/types";
14+
import { ApolloLink, FetchResult, GraphQLRequest } from "../../../link/core";
15+
import {
16+
Observable,
17+
Observer,
18+
} from "../../../utilities/observables/Observable";
1919

2020
// mocks
21-
import mockQueryManager from "../../../testing/core/mocking/mockQueryManager";
22-
import mockWatchQuery from "../../../testing/core/mocking/mockWatchQuery";
2321
import {
2422
MockApolloLink,
2523
mockSingleLink,
2624
} from "../../../testing/core/mocking/mockLink";
25+
import mockQueryManager from "../../../testing/core/mocking/mockQueryManager";
26+
import mockWatchQuery from "../../../testing/core/mocking/mockWatchQuery";
2727

2828
// core
29-
import { ApolloQueryResult } from "../../types";
30-
import { NetworkStatus } from "../../networkStatus";
3129
import { ObservableQuery } from "../../ObservableQuery";
30+
import { QueryManager } from "../../QueryManager";
31+
import { NetworkStatus } from "../../networkStatus";
32+
import { ApolloQueryResult } from "../../types";
3233
import {
3334
MutationBaseOptions,
3435
MutationOptions,
3536
WatchQueryOptions,
3637
} from "../../watchQueryOptions";
37-
import { QueryManager } from "../../QueryManager";
3838

3939
import { ApolloError } from "../../../errors";
4040

4141
// testing utils
4242
import { waitFor } from "@testing-library/react";
43-
import wrap from "../../../testing/core/wrap";
43+
import { ApolloClient } from "../../../core";
44+
import { itAsync, subscribeAndCount } from "../../../testing/core";
4445
import observableToPromise, {
4546
observableToPromiseAndSubscription,
4647
} from "../../../testing/core/observableToPromise";
47-
import { itAsync, subscribeAndCount } from "../../../testing/core";
48-
import { ApolloClient } from "../../../core";
49-
import { mockFetchQuery } from "../ObservableQuery";
48+
import wrap from "../../../testing/core/wrap";
5049
import { Concast, print } from "../../../utilities";
50+
import { mockFetchQuery } from "../ObservableQuery";
5151

5252
interface MockedMutation {
5353
reject: (reason: any) => any;
@@ -4878,6 +4878,44 @@ describe("QueryManager", () => {
48784878
});
48794879
});
48804880

4881+
itAsync(
4882+
"will not update inactive query on `resetStore`",
4883+
(resolve, reject) => {
4884+
const testQuery = gql`
4885+
query {
4886+
author {
4887+
firstName
4888+
lastName
4889+
}
4890+
}
4891+
`;
4892+
const link = new (class extends ApolloLink {
4893+
public request() {
4894+
reject(new Error("Query was not supposed to be called"));
4895+
return null;
4896+
}
4897+
})();
4898+
4899+
const queryManager = new QueryManager({
4900+
link,
4901+
cache: new InMemoryCache({ addTypename: false }),
4902+
});
4903+
const oq = queryManager.watchQuery({
4904+
query: testQuery,
4905+
fetchPolicy: "cache-and-network",
4906+
});
4907+
// Recreate state where an observable query is dirty but has no observers in the query manager
4908+
// @ts-expect-error -- Accessing private field for testing
4909+
oq.queryInfo.dirty = true;
4910+
4911+
resetStore(queryManager).then((q) => {
4912+
expect(q).toHaveLength(0);
4913+
expect(oq.hasObservers()).toBe(false);
4914+
resolve();
4915+
});
4916+
}
4917+
);
4918+
48814919
itAsync(
48824920
"will be true when partial data may be returned",
48834921
(resolve, reject) => {

0 commit comments

Comments
 (0)