diff --git a/Changelog.md b/Changelog.md index 9bf8f7bd72..804b0aa16f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,6 +5,7 @@ Expect active development and potentially significant breaking changes in the `0 ### 1.0.0 - ApolloProvider now won't put its `store` on `context` unless it was given. [PR #550](https://github.com/apollographql/react-apollo/pull/550) - MockedProvider now accepts a `store` prop to be passed to ApolloProvider so that react-redux store is not overwritten +- Make sure recycled queries are in cache only mode so they do not trigger network requests. [PR #531](https://github.com/apollographql/react-apollo/pull/531) ### 1.0.0-rc.3 - Fix bug where `options` was mutated causing variables to not update appropriately. [PR #537](https://github.com/apollographql/react-apollo/pull/537) diff --git a/src/graphql.tsx b/src/graphql.tsx index f03c8781b4..47475b82f4 100644 --- a/src/graphql.tsx +++ b/src/graphql.tsx @@ -647,7 +647,10 @@ class ObservableQueryRecycler { public recycle (observableQuery: ObservableQuery): void { // Stop the query from polling when we recycle. Polling may resume when we // reuse it and call `setOptions`. - observableQuery.stopPolling(); + observableQuery.setOptions({ + fetchPolicy: 'cache-only', + pollInterval: 0, + }); this.observableQueries.push({ observableQuery, @@ -678,7 +681,13 @@ class ObservableQueryRecycler { // Therefore we need to set the new options. // // If this observable query used to poll then polling will be restarted. - observableQuery.setOptions(options); + observableQuery.setOptions({ + ...options, + // Explicitly set options changed when recycling to make sure they + // are set to `undefined` if not provided in options. + pollInterval: options.pollInterval, + fetchPolicy: options.fetchPolicy, + }); return observableQuery; } diff --git a/test/react-web/client/graphql/queries.test.tsx b/test/react-web/client/graphql/queries.test.tsx index 2881d8ec56..032666b8fa 100644 --- a/test/react-web/client/graphql/queries.test.tsx +++ b/test/react-web/client/graphql/queries.test.tsx @@ -5,8 +5,8 @@ import * as ReactDOM from 'react-dom'; import * as renderer from 'react-test-renderer'; import { mount } from 'enzyme'; import gql from 'graphql-tag'; - -import ApolloClient, { ApolloError } from 'apollo-client'; +import ApolloClient, { ApolloError, ObservableQuery } from 'apollo-client'; +import { NetworkInterface } from 'apollo-client/transport/networkInterface'; import { connect } from 'react-redux'; import { withState } from 'recompose'; @@ -2157,7 +2157,9 @@ describe('queries', () => { ); expect(Object.keys((client as any).queryManager.observableQueries)).toEqual(['1']); - const queryObservable1 = (client as any).queryManager.observableQueries['1'].observableQuery; + const queryObservable1: ObservableQuery = (client as any).queryManager.observableQueries['1'].observableQuery; + + const originalOptions = Object.assign({}, queryObservable1.options); wrapper1.unmount(); @@ -2170,15 +2172,88 @@ describe('queries', () => { ); expect(Object.keys((client as any).queryManager.observableQueries)).toEqual(['1']); - const queryObservable2 = (client as any).queryManager.observableQueries['1'].observableQuery; + const queryObservable2: ObservableQuery = (client as any).queryManager.observableQueries['1'].observableQuery; + + const recycledOptions = queryObservable2.options; expect(queryObservable1).toBe(queryObservable2); + expect(recycledOptions).toEqual(originalOptions); wrapper2.unmount(); expect(Object.keys((client as any).queryManager.observableQueries)).toEqual(['1']); }); + it('will not try to refetch recycled `ObservableQuery`s when resetting the client store', () => { + const query = gql`query people { allPeople(first: 1) { people { name } } }`; + const data = { allPeople: { people: [ { name: 'Luke Skywalker' } ] } }; + const networkInterface = { + query: jest.fn(), + } as NetworkInterface; + const client = new ApolloClient({ networkInterface, addTypename: false }); + + @graphql(query) + class Container extends React.Component { + render () { + return null; + } + } + + const wrapper1 = renderer.create( + + + + ); + + expect(Object.keys((client as any).queryManager.observableQueries)).toEqual(['1']); + const queryObservable1 = (client as any).queryManager.observableQueries['1'].observableQuery; + + // The query should only have been invoked when first mounting and not when resetting store + expect(networkInterface.query).toHaveBeenCalledTimes(1); + + wrapper1.unmount(); + + expect(Object.keys((client as any).queryManager.observableQueries)).toEqual(['1']); + const queryObservable2 = (client as any).queryManager.observableQueries['1'].observableQuery; + + expect(queryObservable1).toBe(queryObservable2); + + client.resetStore(); + + // The query should not have been fetch again + expect(networkInterface.query).toHaveBeenCalledTimes(1); + }); + + it('will refetch active `ObservableQuery`s when resetting the client store', () => { + const query = gql`query people { allPeople(first: 1) { people { name } } }`; + const data = { allPeople: { people: [ { name: 'Luke Skywalker' } ] } }; + const networkInterface = { + query: jest.fn(), + } as NetworkInterface; + const client = new ApolloClient({ networkInterface, addTypename: false }); + + @graphql(query) + class Container extends React.Component { + render () { + return null; + } + } + + const wrapper1 = renderer.create( + + + + ); + + expect(Object.keys((client as any).queryManager.observableQueries)).toEqual(['1']); + + expect(networkInterface.query).toHaveBeenCalledTimes(1); + + client.resetStore(); + + expect(networkInterface.query).toHaveBeenCalledTimes(2); + }); + it('will recycle `ObservableQuery`s when re-rendering a portion of the tree', done => { const query = gql`query people { allPeople(first: 1) { people { name } } }`; const data = { allPeople: { people: [ { name: 'Luke Skywalker' } ] } };