diff --git a/packages/hooks/src/__tests__/useQuery.test.tsx b/packages/hooks/src/__tests__/useQuery.test.tsx
index b457ef88d4..5b8205b766 100644
--- a/packages/hooks/src/__tests__/useQuery.test.tsx
+++ b/packages/hooks/src/__tests__/useQuery.test.tsx
@@ -1,7 +1,7 @@
import React, { useState } from 'react';
import { DocumentNode, GraphQLError } from 'graphql';
import gql from 'graphql-tag';
-import { MockedProvider } from '@apollo/react-testing';
+import { MockedProvider, MockLink } from '@apollo/react-testing';
import { render, cleanup } from '@testing-library/react';
import { useQuery } from '@apollo/react-hooks';
@@ -143,6 +143,51 @@ describe('useQuery Hook', () => {
);
});
+ it('should stop polling when the component is unmounted', done => {
+ const mockLink = new MockLink(CAR_MOCKS);
+ const linkRequestSpy = jest.spyOn(mockLink, 'request');
+ let renderCount = 0;
+ const QueryComponent = ({ unmount }: { unmount: () => void }) => {
+ const { data, loading } = useQuery(CAR_QUERY, { pollInterval: 10 });
+ switch (renderCount) {
+ case 0:
+ expect(loading).toBeTruthy();
+ break;
+ case 1:
+ expect(loading).toBeFalsy();
+ expect(data).toEqual(CAR_RESULT_DATA);
+ expect(linkRequestSpy).toHaveBeenCalledTimes(1);
+ break;
+ case 2:
+ expect(loading).toBeFalsy();
+ expect(data).toEqual(CAR_RESULT_DATA);
+ expect(linkRequestSpy).toHaveBeenCalledTimes(2);
+ unmount();
+ break;
+ default:
+ }
+ renderCount += 1;
+ return null;
+ };
+
+ const Component = () => {
+ const [queryMounted, setQueryMounted] = useState(true);
+ const unmount = () => setTimeout(() => setQueryMounted(false), 0);
+ if (!queryMounted)
+ setTimeout(() => {
+ expect(linkRequestSpy).toHaveBeenCalledTimes(2);
+ done();
+ }, 30);
+ return <>{queryMounted && }>;
+ };
+
+ render(
+
+
+
+ );
+ });
+
it('should set called to true by default', () => {
const Component = () => {
const { loading, called } = useQuery(CAR_QUERY);
diff --git a/packages/hooks/src/data/QueryData.ts b/packages/hooks/src/data/QueryData.ts
index ab873aeed8..a52ba95600 100644
--- a/packages/hooks/src/data/QueryData.ts
+++ b/packages/hooks/src/data/QueryData.ts
@@ -112,6 +112,15 @@ export class QueryData extends OperationData {
this.isMounted = true;
if (!lazy || this.runLazy) {
this.handleErrorOrCompleted();
+
+ // When the component is done rendering stored query errors, we'll
+ // remove those errors from the `ObservableQuery` query store, so they
+ // aren't re-displayed on subsequent (potentially error free)
+ // requests/responses.
+ setTimeout(() => {
+ this.currentObservable.query &&
+ this.currentObservable.query.resetQueryStoreErrors();
+ });
}
return this.unmount.bind(this);
}
@@ -383,14 +392,6 @@ export class QueryData extends OperationData {
}
}
- // When the component is done rendering stored query errors, we'll
- // remove those errors from the `ObservableQuery` query store, so they
- // aren't re-displayed on subsequent (potentially error free)
- // requests/responses.
- setTimeout(() => {
- this.currentObservable.query!.resetQueryStoreErrors();
- });
-
result.client = this.client;
this.previousData.loading =
(this.previousData.result && this.previousData.result.loading) || false;
diff --git a/packages/hooks/src/utils/useBaseQuery.ts b/packages/hooks/src/utils/useBaseQuery.ts
index dbf68adb97..515534efcc 100644
--- a/packages/hooks/src/utils/useBaseQuery.ts
+++ b/packages/hooks/src/utils/useBaseQuery.ts
@@ -41,5 +41,9 @@ export function useBaseQuery(
useEffect(() => queryData.afterExecute({ lazy }), [result]);
+ useEffect(() => {
+ return () => queryData.cleanup();
+ }, []);
+
return result;
}
diff --git a/packages/testing/src/mocks/MockedProvider.tsx b/packages/testing/src/mocks/MockedProvider.tsx
index 9bbde50fcd..87917b54b7 100644
--- a/packages/testing/src/mocks/MockedProvider.tsx
+++ b/packages/testing/src/mocks/MockedProvider.tsx
@@ -3,6 +3,7 @@ import { ApolloClient, DefaultOptions, Resolvers } from 'apollo-client';
import { ApolloCache } from 'apollo-cache';
import { InMemoryCache as Cache } from 'apollo-cache-inmemory';
import { ApolloProvider } from '@apollo/react-common';
+import { ApolloLink } from 'apollo-link';
import { MockLink } from './mockLink';
import { MockedResponse } from './types';
@@ -14,6 +15,7 @@ export interface MockedProviderProps {
resolvers?: Resolvers;
childProps?: object;
children?: React.ReactElement;
+ link?: ApolloLink;
}
export interface MockedProviderState {
@@ -23,7 +25,7 @@ export interface MockedProviderState {
export class MockedProvider extends React.Component<
MockedProviderProps,
MockedProviderState
- > {
+> {
public static defaultProps: MockedProviderProps = {
addTypename: true
};
@@ -31,11 +33,18 @@ export class MockedProvider extends React.Component<
constructor(props: MockedProviderProps) {
super(props);
- const { mocks, addTypename, defaultOptions, cache, resolvers } = this.props;
+ const {
+ mocks,
+ addTypename,
+ defaultOptions,
+ cache,
+ resolvers,
+ link
+ } = this.props;
const client = new ApolloClient({
cache: cache || new Cache({ addTypename }),
defaultOptions,
- link: new MockLink(mocks || [], addTypename),
+ link: link || new MockLink(mocks || [], addTypename),
resolvers
});