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

Better support for multiple useLazyQuery execution function calls #3453

Merged
merged 3 commits into from Sep 3, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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