Skip to content

Commit 8b1de2e

Browse files
authored
Fix value of ObservableQuery.variables to be undefined when variables are empty (#12543)
1 parent 90de488 commit 8b1de2e

26 files changed

+883
-470
lines changed

.api-reports/api-report-core.api.md

+6-6
Original file line numberDiff line numberDiff line change
@@ -1234,7 +1234,7 @@ interface MutationStoreValue {
12341234
// (undocumented)
12351235
mutation: DocumentNode_2;
12361236
// (undocumented)
1237-
variables: Record<string, any>;
1237+
variables: Record<string, any> | undefined;
12381238
}
12391239

12401240
// @public @deprecated (undocumented)
@@ -1982,7 +1982,7 @@ export interface WatchQueryOptions<TVariables extends OperationVariables = Opera
19821982
refetchWritePolicy?: RefetchWritePolicy;
19831983
returnPartialData?: boolean;
19841984
skipPollAttempt?: () => boolean;
1985-
variables?: TVariables;
1985+
variables?: NoInfer_2<TVariables>;
19861986
}
19871987

19881988
// @public (undocumented)
@@ -2025,10 +2025,10 @@ interface WriteContext extends ReadMergeModifyContext {
20252025
// src/cache/inmemory/policies.ts:167:3 - (ae-forgotten-export) The symbol "FieldReadFunction_2" needs to be exported by the entry point index.d.ts
20262026
// src/cache/inmemory/policies.ts:168:3 - (ae-forgotten-export) The symbol "FieldMergeFunction_2" needs to be exported by the entry point index.d.ts
20272027
// src/cache/inmemory/types.ts:133:3 - (ae-forgotten-export) The symbol "KeyFieldsFunction" needs to be exported by the entry point index.d.ts
2028-
// src/core/ObservableQuery.ts:140:5 - (ae-forgotten-export) The symbol "QueryManager" needs to be exported by the entry point index.d.ts
2029-
// src/core/ObservableQuery.ts:141:5 - (ae-forgotten-export) The symbol "QueryInfo" needs to be exported by the entry point index.d.ts
2030-
// src/core/QueryManager.ts:184:5 - (ae-forgotten-export) The symbol "MutationStoreValue" needs to be exported by the entry point index.d.ts
2031-
// src/core/QueryManager.ts:454:7 - (ae-forgotten-export) The symbol "UpdateQueries" needs to be exported by the entry point index.d.ts
2028+
// src/core/ObservableQuery.ts:143:5 - (ae-forgotten-export) The symbol "QueryManager" needs to be exported by the entry point index.d.ts
2029+
// src/core/ObservableQuery.ts:144:5 - (ae-forgotten-export) The symbol "QueryInfo" needs to be exported by the entry point index.d.ts
2030+
// src/core/QueryManager.ts:188:5 - (ae-forgotten-export) The symbol "MutationStoreValue" needs to be exported by the entry point index.d.ts
2031+
// src/core/QueryManager.ts:460:7 - (ae-forgotten-export) The symbol "UpdateQueries" needs to be exported by the entry point index.d.ts
20322032

20332033
// (No @packageDocumentation comment for this package)
20342034

.api-reports/api-report-react.api.md

+6-6
Original file line numberDiff line numberDiff line change
@@ -510,7 +510,7 @@ interface MutationStoreValue {
510510
// (undocumented)
511511
mutation: DocumentNode;
512512
// (undocumented)
513-
variables: Record<string, any>;
513+
variables: Record<string, any> | undefined;
514514
}
515515

516516
// @public @deprecated (undocumented)
@@ -1598,16 +1598,16 @@ interface WatchQueryOptions_2<TVariables extends OperationVariables_2 = Operatio
15981598
refetchWritePolicy?: RefetchWritePolicy;
15991599
returnPartialData?: boolean;
16001600
skipPollAttempt?: () => boolean;
1601-
variables?: TVariables;
1601+
variables?: NoInfer_2<TVariables>;
16021602
}
16031603

16041604
// Warnings were encountered during analysis:
16051605
//
16061606
// src/core/LocalState.ts:71:3 - (ae-forgotten-export) The symbol "ApolloClient_2" needs to be exported by the entry point index.d.ts
1607-
// src/core/ObservableQuery.ts:140:5 - (ae-forgotten-export) The symbol "QueryManager" needs to be exported by the entry point index.d.ts
1608-
// src/core/ObservableQuery.ts:141:5 - (ae-forgotten-export) The symbol "QueryInfo" needs to be exported by the entry point index.d.ts
1609-
// src/core/QueryManager.ts:184:5 - (ae-forgotten-export) The symbol "MutationStoreValue" needs to be exported by the entry point index.d.ts
1610-
// src/core/QueryManager.ts:454:7 - (ae-forgotten-export) The symbol "UpdateQueries" needs to be exported by the entry point index.d.ts
1607+
// src/core/ObservableQuery.ts:143:5 - (ae-forgotten-export) The symbol "QueryManager" needs to be exported by the entry point index.d.ts
1608+
// src/core/ObservableQuery.ts:144:5 - (ae-forgotten-export) The symbol "QueryInfo" needs to be exported by the entry point index.d.ts
1609+
// src/core/QueryManager.ts:188:5 - (ae-forgotten-export) The symbol "MutationStoreValue" needs to be exported by the entry point index.d.ts
1610+
// src/core/QueryManager.ts:460:7 - (ae-forgotten-export) The symbol "UpdateQueries" needs to be exported by the entry point index.d.ts
16111611
// src/core/types.ts:204:3 - (ae-forgotten-export) The symbol "MutationQueryReducer" needs to be exported by the entry point index.d.ts
16121612
// src/core/types.ts:233:5 - (ae-forgotten-export) The symbol "Resolver" needs to be exported by the entry point index.d.ts
16131613
// src/core/watchQueryOptions.ts:195:3 - (ae-forgotten-export) The symbol "UpdateQueryOptions" needs to be exported by the entry point index.d.ts

.api-reports/api-report-utilities_internal.api.md

+3
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ const globalCaches: {
7676
canonicalStringify?: () => number;
7777
};
7878

79+
// @public
80+
export function normalizeVariables<TVariables>(variables: TVariables): NonNullable<TVariables> | undefined;
81+
7982
// @public (undocumented)
8083
type ObservableEvent<T> = {
8184
type: "complete";

.api-reports/api-report.api.md

+6-6
Original file line numberDiff line numberDiff line change
@@ -1520,7 +1520,7 @@ interface MutationStoreValue {
15201520
// (undocumented)
15211521
mutation: DocumentNode;
15221522
// (undocumented)
1523-
variables: Record<string, any>;
1523+
variables: Record<string, any> | undefined;
15241524
}
15251525

15261526
// @public @deprecated (undocumented)
@@ -2437,7 +2437,7 @@ export interface WatchQueryOptions<TVariables extends OperationVariables = Opera
24372437
refetchWritePolicy?: RefetchWritePolicy;
24382438
returnPartialData?: boolean;
24392439
skipPollAttempt?: () => boolean;
2440-
variables?: TVariables;
2440+
variables?: NoInfer_2<TVariables>;
24412441
}
24422442

24432443
// @public (undocumented)
@@ -2479,10 +2479,10 @@ interface WriteContext extends ReadMergeModifyContext {
24792479
// src/cache/inmemory/policies.ts:166:3 - (ae-forgotten-export) The symbol "KeySpecifier" needs to be exported by the entry point index.d.ts
24802480
// src/cache/inmemory/policies.ts:166:3 - (ae-forgotten-export) The symbol "KeyArgsFunction" needs to be exported by the entry point index.d.ts
24812481
// src/cache/inmemory/types.ts:133:3 - (ae-forgotten-export) The symbol "KeyFieldsFunction" needs to be exported by the entry point index.d.ts
2482-
// src/core/ObservableQuery.ts:140:5 - (ae-forgotten-export) The symbol "QueryManager" needs to be exported by the entry point index.d.ts
2483-
// src/core/ObservableQuery.ts:141:5 - (ae-forgotten-export) The symbol "QueryInfo" needs to be exported by the entry point index.d.ts
2484-
// src/core/QueryManager.ts:184:5 - (ae-forgotten-export) The symbol "MutationStoreValue" needs to be exported by the entry point index.d.ts
2485-
// src/core/QueryManager.ts:454:7 - (ae-forgotten-export) The symbol "UpdateQueries" needs to be exported by the entry point index.d.ts
2482+
// src/core/ObservableQuery.ts:143:5 - (ae-forgotten-export) The symbol "QueryManager" needs to be exported by the entry point index.d.ts
2483+
// src/core/ObservableQuery.ts:144:5 - (ae-forgotten-export) The symbol "QueryInfo" needs to be exported by the entry point index.d.ts
2484+
// src/core/QueryManager.ts:188:5 - (ae-forgotten-export) The symbol "MutationStoreValue" needs to be exported by the entry point index.d.ts
2485+
// src/core/QueryManager.ts:460:7 - (ae-forgotten-export) The symbol "UpdateQueries" needs to be exported by the entry point index.d.ts
24862486
// src/link/http/selectHttpOptionsAndBody.ts:128:1 - (ae-forgotten-export) The symbol "HttpQueryOptions" needs to be exported by the entry point index.d.ts
24872487

24882488
// (No @packageDocumentation comment for this package)

.changeset/fair-ways-enjoy.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@apollo/client": patch
3+
---
4+
5+
`ObservableQuery.variables` now returns `undefined` if no variables were set from `watchQuery` or if `variables` was set to an empty object. This ensures the value of `variables` lines up with the type.
6+
7+
This change also propogates to `useQuery` and `useLazyQuery`. The `variables` property returned from these hooks will be `undefined` instead of an empty object if no `variables` were set.

.changeset/sixty-meals-travel.md

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
"@apollo/client": minor
3+
---
4+
5+
`ObservableQuery` `variables` can now be reset back to `undefined` by calling `reobserve` with a `variables` key set to `undefined`:
6+
7+
```ts
8+
observable.reobserve({ variables: undefined });
9+
```
10+
11+
Previously this would leave `variables` unchanged and would require setting `variables` to an empty object.
12+
13+
Calling `reobserve({ variables: {} })` has the same effect as `undefined` and will reset `ObservableQuery.variables` back to `undefined`.

.changeset/sour-starfishes-pump.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@apollo/client": patch
3+
---
4+
5+
Ensure `variables` passed to the `update` callback is `undefined` instead of an empty object when `variables` are not defined.

.size-limits.json

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
2-
"import { ApolloClient, InMemoryCache, HttpLink } from \"@apollo/client\" (CJS)": 42984,
3-
"import { ApolloClient, InMemoryCache, HttpLink } from \"@apollo/client\" (production) (CJS)": 38432,
4-
"import { ApolloClient, InMemoryCache, HttpLink } from \"@apollo/client\"": 32968,
5-
"import { ApolloClient, InMemoryCache, HttpLink } from \"@apollo/client\" (production)": 27861
2+
"import { ApolloClient, InMemoryCache, HttpLink } from \"@apollo/client\" (CJS)": 43112,
3+
"import { ApolloClient, InMemoryCache, HttpLink } from \"@apollo/client\" (production) (CJS)": 38549,
4+
"import { ApolloClient, InMemoryCache, HttpLink } from \"@apollo/client\"": 32997,
5+
"import { ApolloClient, InMemoryCache, HttpLink } from \"@apollo/client\" (production)": 27868
66
}

src/__tests__/__snapshots__/exports.ts.snap

+1
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,7 @@ Array [
487487
"getApolloCacheMemoryInternals",
488488
"getApolloClientMemoryInternals",
489489
"getInMemoryCacheMemoryInternals",
490+
"normalizeVariables",
490491
"onAnyEvent",
491492
"registerGlobalCache",
492493
"toQueryResult",

src/__tests__/dataMasking.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -5086,7 +5086,7 @@ describe("observableQuery.subscribeToMore", () => {
50865086
},
50875087
{
50885088
complete: true,
5089-
variables: {},
5089+
variables: undefined,
50905090
previousData: {
50915091
recentComment: {
50925092
__typename: "Comment",
@@ -5229,7 +5229,7 @@ describe("observableQuery.subscribeToMore", () => {
52295229
},
52305230
{
52315231
complete: true,
5232-
variables: {},
5232+
variables: undefined,
52335233
previousData: {
52345234
recentComment: {
52355235
__typename: "Comment",
@@ -5378,7 +5378,7 @@ describe("observableQuery.subscribeToMore", () => {
53785378
},
53795379
{
53805380
complete: true,
5381-
variables: {},
5381+
variables: undefined,
53825382
previousData: {
53835383
recentComment: {
53845384
__typename: "Comment",
@@ -5671,7 +5671,7 @@ describe("client.mutate", () => {
56715671
},
56725672
},
56735673
},
5674-
{ context: undefined, variables: {} }
5674+
{ context: undefined, variables: undefined }
56755675
);
56765676
});
56775677

src/__tests__/mutationResults.ts

+35-11
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { InMemoryCache } from "@apollo/client/cache";
1111
import { CombinedGraphQLErrors } from "@apollo/client/errors";
1212
import { ApolloLink } from "@apollo/client/link/core";
1313
import type { MockedResponse } from "@apollo/client/testing";
14-
import { mockSingleLink } from "@apollo/client/testing";
14+
import { MockLink, mockSingleLink } from "@apollo/client/testing";
1515
import {
1616
ObservableStream,
1717
spyOnConsole,
@@ -646,16 +646,40 @@ describe("mutation results", () => {
646646
},
647647
};
648648

649-
const { client, obsQuery } = setupObsQuery(
650-
{
651-
request: { query: queryTodos },
652-
result: queryTodosResult,
653-
},
654-
{
655-
request: { query: mutationTodo },
656-
result: mutationTodoResult,
657-
}
658-
);
649+
const client = new ApolloClient({
650+
link: new MockLink(
651+
[
652+
{
653+
request: { query: queryWithTypename } as any,
654+
result,
655+
},
656+
{
657+
request: { query: queryTodos },
658+
result: queryTodosResult,
659+
},
660+
{
661+
request: { query: mutationTodo },
662+
result: mutationTodoResult,
663+
},
664+
],
665+
{ defaultOptions: { delay: 0 } }
666+
),
667+
cache: new InMemoryCache({
668+
dataIdFromObject: (obj: any) => {
669+
if (obj.id && obj.__typename) {
670+
return obj.__typename + obj.id;
671+
}
672+
return null;
673+
},
674+
// Passing an empty map enables warnings about missing fields:
675+
possibleTypes: {},
676+
}),
677+
});
678+
679+
const obsQuery = client.watchQuery({
680+
query,
681+
notifyOnNetworkStatusChange: false,
682+
});
659683

660684
await firstValueFrom(from(obsQuery));
661685

src/core/ObservableQuery.ts

+26-6
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@ import {
2121
preventUnhandledRejection,
2222
} from "@apollo/client/utilities";
2323
import { __DEV__ } from "@apollo/client/utilities/environment";
24-
import { toQueryResult } from "@apollo/client/utilities/internal";
24+
import {
25+
normalizeVariables,
26+
toQueryResult,
27+
} from "@apollo/client/utilities/internal";
2528
import { invariant } from "@apollo/client/utilities/invariant";
2629

2730
import { equalByQuery } from "./equalByQuery.js";
@@ -227,6 +230,7 @@ export class ObservableQuery<
227230
initialFetchPolicy = fetchPolicy === "standby" ? defaultFetchPolicy : (
228231
fetchPolicy
229232
),
233+
variables,
230234
} = options;
231235

232236
this.options = {
@@ -240,6 +244,10 @@ export class ObservableQuery<
240244
// This ensures this.options.fetchPolicy always has a string value, in
241245
// case options.fetchPolicy was not provided.
242246
fetchPolicy,
247+
248+
// `variables` might be an empty object due to QueryManager.getVariables,
249+
// so we reset to `undefined` if there are no values
250+
variables: normalizeVariables(variables),
243251
};
244252

245253
this.queryId = queryInfo.queryId || queryManager.generateQueryId();
@@ -787,6 +795,8 @@ Did you mean to call refetch(variables) instead of refetch({ variables })?`,
787795
public async setVariables(
788796
variables: TVariables
789797
): Promise<QueryResult<TData>> {
798+
variables = normalizeVariables(variables) as TVariables;
799+
790800
if (equal(this.variables, variables)) {
791801
// If we have no observers, then we don't actually want to make a network
792802
// request. As soon as someone observes the query, the request will kick
@@ -1043,6 +1053,15 @@ Did you mean to call refetch(variables) instead of refetch({ variables })?`,
10431053
const oldFetchPolicy = this.options.fetchPolicy;
10441054

10451055
const mergedOptions = compact(this.options, newOptions || {});
1056+
1057+
// Allow variables to be reset back to `undefined` if explicitly passed as
1058+
// variables: undefined to reobserve, otherwise `compact` will ignore the
1059+
// `variables` key from from `newOptions`.
1060+
mergedOptions.variables =
1061+
newOptions && "variables" in newOptions ?
1062+
newOptions.variables
1063+
: this.options.variables;
1064+
10461065
const options =
10471066
useDisposableObservable ?
10481067
// Disposable Observable fetches receive a shallow copy of this.options
@@ -1057,6 +1076,10 @@ Did you mean to call refetch(variables) instead of refetch({ variables })?`,
10571076
const query = this.transformDocument(options.query);
10581077
const { fetchPolicy } = options;
10591078

1079+
// note: This may mutate this.options.variables when not using a disposable
1080+
// observable. This is intentional
1081+
options.variables = normalizeVariables(options.variables);
1082+
10601083
this.lastQuery = query;
10611084

10621085
if (!useDisposableObservable) {
@@ -1066,9 +1089,7 @@ Did you mean to call refetch(variables) instead of refetch({ variables })?`,
10661089
// Reset options.fetchPolicy to its original value when variables change,
10671090
// unless a new fetchPolicy was provided by newOptions.
10681091
if (
1069-
newOptions &&
1070-
newOptions.variables &&
1071-
!equal(newOptions.variables, oldVariables) &&
1092+
!equal(options.variables, oldVariables) &&
10721093
// Don't mess with the fetchPolicy if it's currently "standby".
10731094
fetchPolicy !== "standby" &&
10741095
// If we're changing the fetchPolicy anyway, don't try to change it here
@@ -1092,8 +1113,7 @@ Did you mean to call refetch(variables) instead of refetch({ variables })?`,
10921113

10931114
if (
10941115
oldNetworkStatus !== NetworkStatus.loading &&
1095-
newOptions?.variables &&
1096-
!equal(newOptions.variables, oldVariables)
1116+
!equal(options.variables, oldVariables)
10971117
) {
10981118
newNetworkStatus = NetworkStatus.setVariables;
10991119
}

0 commit comments

Comments
 (0)