Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[pull] main from apollographql:main #166

Merged
merged 9 commits into from
May 29, 2022
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## Apollo Client 3.6.6 (2022-05-26)

### Bug Fixes

- Allow `useLazyQuery(query, { defaultOptions })` to benefit from `defaultOptions.variables` and `client.defaultOptions.watchQuery.variables` merging. <br/>
[@benjamn](https://github.com/benjamn) in [#9762](https://github.com/apollographql/apollo-client/pull/9762)

## Apollo Client 3.6.5 (2022-05-23)

### Bug Fixes
Expand Down
32 changes: 16 additions & 16 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@apollo/client",
"version": "3.6.5",
"version": "3.6.6",
"description": "A fully-featured caching GraphQL client.",
"private": true,
"keywords": [
Expand Down Expand Up @@ -56,7 +56,7 @@
{
"name": "apollo-client",
"path": "./dist/apollo-client.min.cjs",
"maxSize": "29.55kB"
"maxSize": "29.6kB"
}
],
"engines": {
Expand Down Expand Up @@ -94,7 +94,7 @@
"zen-observable-ts": "^1.2.5"
},
"devDependencies": {
"@babel/parser": "7.18.0",
"@babel/parser": "7.18.3",
"@graphql-tools/schema": "8.3.13",
"@rollup/plugin-node-resolve": "11.2.1",
"@testing-library/react": "12.1.5",
Expand Down Expand Up @@ -126,7 +126,7 @@
"recast": "0.21.1",
"resolve": "1.22.0",
"rimraf": "3.0.2",
"rollup": "2.74.1",
"rollup": "2.75.1",
"rollup-plugin-terser": "7.0.2",
"rxjs": "7.5.5",
"subscriptions-transport-ws": "0.11.0",
Expand Down
98 changes: 95 additions & 3 deletions src/react/hooks/__tests__/useLazyQuery.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -229,12 +229,16 @@ describe('useLazyQuery Hook', () => {
const counterQuery: TypedDocumentNode<{
counter: number;
}, {
hookVar?: any;
execVar?: any;
hookVar?: boolean;
execVar?: boolean;
localDefaultVar?: boolean;
globalDefaultVar?: boolean;
}> = gql`
query GetCounter (
$hookVar: Boolean
$execVar: Boolean
$localDefaultVar: Boolean
$globalDefaultVar: Boolean
) {
counter
vars
Expand All @@ -243,6 +247,13 @@ describe('useLazyQuery Hook', () => {

let count = 0;
const client = new ApolloClient({
defaultOptions: {
watchQuery: {
variables: {
globalDefaultVar: true,
},
},
},
cache: new InMemoryCache(),
link: new ApolloLink(request => new Observable(observer => {
if (request.operationName === "GetCounter") {
Expand Down Expand Up @@ -270,6 +281,11 @@ describe('useLazyQuery Hook', () => {
variables: {
hookVar: true,
},
defaultOptions: {
variables: {
localDefaultVar: true,
},
},
});
return {
exec,
Expand All @@ -296,6 +312,8 @@ describe('useLazyQuery Hook', () => {
const expectedFinalData = {
counter: 1,
vars: {
globalDefaultVar: true,
localDefaultVar: true,
hookVar: true,
execVar: true,
},
Expand All @@ -304,7 +322,7 @@ describe('useLazyQuery Hook', () => {
const execPromise = act(() => result.current.exec({
variables: {
execVar: true,
}
},
}).then(finalResult => {
expect(finalResult.loading).toBe(false);
expect(finalResult.called).toBe(true);
Expand All @@ -324,6 +342,80 @@ describe('useLazyQuery Hook', () => {
await expect(waitForNextUpdate({
timeout: 20,
})).rejects.toThrow('Timed out');

const refetchPromise = act(() => result.current.query.reobserve({
fetchPolicy: "network-only",
nextFetchPolicy: "cache-first",
variables: {
execVar: false,
},
}).then(finalResult => {
expect(finalResult.loading).toBe(false);
expect(finalResult.data).toEqual({
counter: 2,
vars: {
execVar: false,
},
});
}));
await waitForNextUpdate();
expect(result.current.query.loading).toBe(false);
expect(result.current.query.called).toBe(true);
expect(result.current.query.data).toEqual({
counter: 2,
vars: {
execVar: false,
},
});

await refetchPromise;

await expect(waitForNextUpdate({
timeout: 20,
})).rejects.toThrow('Timed out');

const execPromise2 = act(() => result.current.exec({
fetchPolicy: "cache-and-network",
nextFetchPolicy: "cache-first",
variables: {
execVar: true,
},
}).then(finalResult => {
expect(finalResult.loading).toBe(false);
expect(finalResult.called).toBe(true);
expect(finalResult.data).toEqual({
counter: 3,
vars: {
...expectedFinalData.vars,
execVar: true,
},
});
}));

expect(result.current.query.loading).toBe(true);
expect(result.current.query.called).toBe(true);
expect(result.current.query.data).toEqual({
counter: 2,
vars: {
execVar: false,
},
});
await waitForNextUpdate();
expect(result.current.query.loading).toBe(false);
expect(result.current.query.called).toBe(true);
expect(result.current.query.data).toEqual({
counter: 3,
vars: {
...expectedFinalData.vars,
execVar: true,
},
});

await execPromise2;

await expect(waitForNextUpdate({
timeout: 20,
})).rejects.toThrow('Timed out');
});

it('should fetch data each time the execution function is called, when using a "network-only" fetch policy', async () => {
Expand Down
44 changes: 35 additions & 9 deletions src/react/hooks/useQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import {

import { DocumentType, verifyDocumentType } from '../parser';
import { useApolloClient } from './useApolloClient';
import { canUseWeakMap, canUseWeakSet, isNonEmptyArray, maybeDeepFreeze } from '../../utilities';
import { canUseWeakMap, canUseWeakSet, compact, isNonEmptyArray, maybeDeepFreeze } from '../../utilities';

const {
prototype: {
Expand Down Expand Up @@ -276,7 +276,7 @@ class InternalState<TData, TVariables> {
// subscriptions, though it does feel less than ideal that reobserve
// (potentially) kicks off a network request (for example, when the
// variables have changed), which is technically a side-effect.
this.observable.reobserve(watchQueryOptions);
this.observable.reobserve(this.getObsQueryOptions());

// Make sure getCurrentResult returns a fresh ApolloQueryResult<TData>,
// but save the current data as this.previousData, just like setResult
Expand Down Expand Up @@ -326,6 +326,38 @@ class InternalState<TData, TVariables> {
}
}

private getObsQueryOptions(): WatchQueryOptions<TVariables, TData> {
const toMerge: Array<
Partial<WatchQueryOptions<TVariables, TData>>
> = [];

const globalDefaults = this.client.defaultOptions.watchQuery;
if (globalDefaults) toMerge.push(globalDefaults);

if (this.queryHookOptions.defaultOptions) {
toMerge.push(this.queryHookOptions.defaultOptions);
}

// We use compact rather than mergeOptions for this part of the merge,
// because we want watchQueryOptions.variables (if defined) to replace
// this.observable.options.variables whole. This replacement allows
// removing variables by removing them from the variables input to
// useQuery. If the variables were always merged together (rather than
// replaced), there would be no way to remove existing variables.
// However, the variables from options.defaultOptions and globalDefaults
// (if provided) should be merged, to ensure individual defaulted
// variables always have values, if not otherwise defined in
// observable.options or watchQueryOptions.
toMerge.push(compact(
this.observable && this.observable.options,
this.watchQueryOptions,
));

return toMerge.reduce(
mergeOptions
) as WatchQueryOptions<TVariables, TData>;
}

private ssrDisabledResult = maybeDeepFreeze({
loading: true,
data: void 0 as unknown as TData,
Expand Down Expand Up @@ -424,13 +456,7 @@ class InternalState<TData, TVariables> {
this.renderPromises
&& this.renderPromises.getSSRObservable(this.watchQueryOptions)
|| this.observable // Reuse this.observable if possible (and not SSR)
|| this.client.watchQuery(mergeOptions(
// Any options.defaultOptions passed to useQuery serve as default
// options because we use them only here, when first creating the
// ObservableQuery by calling client.watchQuery.
this.queryHookOptions.defaultOptions,
this.watchQueryOptions,
));
|| this.client.watchQuery(this.getObsQueryOptions());

this.obsQueryFields = useMemo(() => ({
refetch: obsQuery.refetch.bind(obsQuery),
Expand Down