Skip to content
This repository has been archived by the owner on Apr 13, 2023. It is now read-only.

Commit

Permalink
Better support for multiple useLazyQuery execution function calls (#…
Browse files Browse the repository at this point in the history
…3453)

* Better support for multiple `useLazyQuery` execution function calls

Before this commit, calling a `useLazyQuery` exection function
multiple times in a row, when using a fetch policy of
`network-only`, lead to unexpected results. Only the first network
request was submitted as Apollo Client was blocking subsequent
requests, since the query, fetch policy and variables remained
the same. This commmit adds additional cleanup to the
`useLazyQuery` exection function, such that each call will start
a new `ObservableQuery` instance. This means each query fired
by the `useLazyQuery` execution function is treated as a fully
new query.

Fixes #3355.

* Changelog update
  • Loading branch information
hwillson committed Sep 3, 2019
1 parent 7d4ec9b commit 3c05b08
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 20 deletions.
2 changes: 2 additions & 0 deletions Changelog.md
Expand Up @@ -16,6 +16,8 @@
[@joshalling](https://github.com/joshalling) in [#3417](https://github.com/apollographql/react-apollo/pull/3417)
- Prevent inline `onError` and `onCompleted` callbacks from being part of the internal memoization that's used to decide when certain after render units of functionality are run, when using `useQuery`. This fixes issues related to un-necessary component cleanup, like `error` disappearing from results when it should be present. <br/>
[@dylanwulf](https://github.com/dylanwulf) in [#3419](https://github.com/apollographql/react-apollo/pull/3419)
- `useLazyQuery`'s execution function can now be called multiple times in a row, and will properly submit network requests each time called, when using a fetch policy of `network-only`. <br/>
[@hwillson](https://github.com/hwillson) in [#3453](https://github.com/apollographql/react-apollo/pull/3453)
- Documentation fixes. <br/>
[@SeanRoberts](https://github.com/SeanRoberts) in [#3380](https://github.com/apollographql/react-apollo/pull/3380)

Expand Down
142 changes: 122 additions & 20 deletions packages/hooks/src/__tests__/useLazyQuery.test.tsx
@@ -1,8 +1,8 @@
import React, { useState } from 'react';
import React from 'react';
import { DocumentNode } from 'graphql';
import gql from 'graphql-tag';
import { MockedProvider } from '@apollo/react-testing';
import { render, cleanup } from '@testing-library/react';
import { render, wait } from '@testing-library/react';
import { ApolloProvider, useLazyQuery } from '@apollo/react-hooks';
import { ApolloClient } from 'apollo-client';
import { InMemoryCache } from 'apollo-cache-inmemory';
Expand Down Expand Up @@ -38,24 +38,23 @@ describe('useLazyQuery Hook', () => {
}
];

afterEach(cleanup);

it('should hold query execution until manually triggered', done => {
it('should hold query execution until manually triggered', async () => {
let renderCount = 0;
const Component = () => {
const [execute, { loading, data }] = useLazyQuery(CAR_QUERY);
switch (renderCount) {
case 0:
expect(loading).toEqual(false);
execute();
setTimeout(() => {
execute();
});
break;
case 1:
expect(loading).toEqual(true);
break;
case 2:
expect(loading).toEqual(false);
expect(data).toEqual(CAR_RESULT_DATA);
done();
break;
default: // Do nothing
}
Expand All @@ -68,11 +67,15 @@ describe('useLazyQuery Hook', () => {
<Component />
</MockedProvider>
);

await wait(() => {
expect(renderCount).toBe(3);
});
});

it('should set `called` to false by default', () => {
const Component = () => {
const [execute, { loading, called }] = useLazyQuery(CAR_QUERY);
const [, { loading, called }] = useLazyQuery(CAR_QUERY);
expect(loading).toBeFalsy();
expect(called).toBeFalsy();
return null;
Expand All @@ -85,15 +88,17 @@ describe('useLazyQuery Hook', () => {
);
});

it('should set `called` to true after calling the lazy execute function', done => {
it('should set `called` to true after calling the lazy execute function', async () => {
let renderCount = 0;
const Component = () => {
const [execute, { loading, called, data }] = useLazyQuery(CAR_QUERY);
switch (renderCount) {
case 0:
expect(loading).toBeFalsy();
expect(called).toBeFalsy();
execute();
setTimeout(() => {
execute();
});
break;
case 1:
expect(loading).toBeTruthy();
Expand All @@ -103,7 +108,6 @@ describe('useLazyQuery Hook', () => {
expect(loading).toEqual(false);
expect(called).toBeTruthy();
expect(data).toEqual(CAR_RESULT_DATA);
done();
break;
default: // Do nothing
}
Expand All @@ -116,9 +120,13 @@ describe('useLazyQuery Hook', () => {
<Component />
</MockedProvider>
);

await wait(() => {
expect(renderCount).toBe(3);
});
});

it('should override `skip` if lazy mode execution function is called', done => {
it('should override `skip` if lazy mode execution function is called', async () => {
let renderCount = 0;
const Component = () => {
const [execute, { loading, data }] = useLazyQuery(CAR_QUERY, {
Expand All @@ -127,15 +135,16 @@ describe('useLazyQuery Hook', () => {
switch (renderCount) {
case 0:
expect(loading).toBeFalsy();
execute();
setTimeout(() => {
execute();
});
break;
case 1:
expect(loading).toBeTruthy();
break;
case 2:
expect(loading).toEqual(false);
expect(data).toEqual(CAR_RESULT_DATA);
done();
break;
default: // Do nothing
}
Expand All @@ -148,12 +157,16 @@ describe('useLazyQuery Hook', () => {
<Component />
</MockedProvider>
);

await wait(() => {
expect(renderCount).toBe(3);
});
});

it(
'should use variables defined in hook options (if any), when running ' +
'the lazy execution function',
done => {
async () => {
const CAR_QUERY: DocumentNode = gql`
query AllCars($year: Int!) {
cars(year: $year) @client {
Expand Down Expand Up @@ -195,15 +208,16 @@ describe('useLazyQuery Hook', () => {
switch (renderCount) {
case 0:
expect(loading).toBeFalsy();
execute();
setTimeout(() => {
execute();
});
break;
case 1:
expect(loading).toBeTruthy();
break;
case 2:
expect(loading).toEqual(false);
expect(data.cars).toEqual([CAR_RESULT_DATA[1]]);
done();
break;
default: // Do nothing
}
Expand All @@ -216,13 +230,17 @@ describe('useLazyQuery Hook', () => {
<Component />
</ApolloProvider>
);

await wait(() => {
expect(renderCount).toBe(3);
});
}
);

it(
'should use variables passed into lazy execution function, ' +
'overriding similar variables defined in Hook options',
done => {
async () => {
const CAR_QUERY: DocumentNode = gql`
query AllCars($year: Int!) {
cars(year: $year) @client {
Expand Down Expand Up @@ -264,15 +282,16 @@ describe('useLazyQuery Hook', () => {
switch (renderCount) {
case 0:
expect(loading).toBeFalsy();
execute({ variables: { year: 2000 } });
setTimeout(() => {
execute({ variables: { year: 2000 } });
});
break;
case 1:
expect(loading).toBeTruthy();
break;
case 2:
expect(loading).toEqual(false);
expect(data.cars).toEqual([CAR_RESULT_DATA[0]]);
done();
break;
default: // Do nothing
}
Expand All @@ -285,6 +304,89 @@ describe('useLazyQuery Hook', () => {
<Component />
</ApolloProvider>
);

await wait(() => {
expect(renderCount).toBe(3);
});
}
);

it(
'should fetch data each time the execution function is called, when ' +
'using a "network-only" fetch policy',
async () => {
const data1 = CAR_RESULT_DATA;

const data2 = {
cars: [
{
make: 'Audi',
model: 'SQ5',
vin: 'POWERANDTRUNKSPACE',
__typename: 'Car'
}
]
};

const mocks = [
{
request: {
query: CAR_QUERY
},
result: { data: data1 }
},
{
request: {
query: CAR_QUERY
},
result: { data: data2 }
}
];

let renderCount = 0;
const Component = () => {
const [execute, { loading, data }] = useLazyQuery(CAR_QUERY, {
fetchPolicy: 'network-only'
});
switch (renderCount) {
case 0:
expect(loading).toEqual(false);
setTimeout(() => {
execute();
});
break;
case 1:
expect(loading).toEqual(true);
break;
case 2:
expect(loading).toEqual(false);
expect(data).toEqual(data1);
setTimeout(() => {
execute();
});
break;
case 3:
expect(loading).toEqual(true);
break;
case 4:
expect(loading).toEqual(false);
expect(data).toEqual(data2);
break;
default: // Do nothing
}
renderCount += 1;
return null;
};

render(
<MockedProvider mocks={mocks}>
<Component />
</MockedProvider>
);

await wait(() => {
expect(renderCount).toBe(5);
});
}
);
});
3 changes: 3 additions & 0 deletions packages/hooks/src/data/QueryData.ts
Expand Up @@ -126,6 +126,7 @@ export class QueryData<TData, TVariables> extends OperationData {
this.currentObservable.query.resetQueryStoreErrors();
});
}

return this.unmount.bind(this);
}

Expand Down Expand Up @@ -159,6 +160,8 @@ export class QueryData<TData, TVariables> extends OperationData {
}

private runLazyQuery = (options?: QueryLazyOptions<TVariables>) => {
this.cleanup();

this.runLazy = true;
this.lazyOptions = options;
this.forceUpdate();
Expand Down

0 comments on commit 3c05b08

Please sign in to comment.