diff --git a/src/Query.tsx b/src/Query.tsx index 9b34b3ac53..639deeaf6b 100644 --- a/src/Query.tsx +++ b/src/Query.tsx @@ -183,7 +183,13 @@ class Query extends React.Component< private startQuerySubscription = () => { this.querySubscription = this.queryObservable.subscribe({ next: this.updateCurrentData, - error: this.updateCurrentData, + error: error => { + this.resubscribeToQuery(); + // Quick fix for https://github.com/apollostack/react-apollo/issues/378 + if (!error.hasOwnProperty('graphQLErrors')) throw error; + + this.updateCurrentData(); + }, }); }; @@ -193,6 +199,20 @@ class Query extends React.Component< } }; + private resubscribeToQuery() { + this.removeQuerySubscription(); + + const lastError = this.queryObservable.getLastError(); + const lastResult = this.queryObservable.getLastResult(); + // If lastError is set, the observable will immediately + // send it, causing the stream to terminate on initialization. + // We clear everything here and restore it afterward to + // make sure the new subscription sticks. + this.queryObservable.resetLastResults(); + this.startQuerySubscription(); + Object.assign(this.queryObservable, { lastError, lastResult }); + } + private updateCurrentData = () => { this.setState({ result: this.queryObservable.currentResult() }); }; diff --git a/test/client/Query.test.tsx b/test/client/Query.test.tsx index 6e14a89fa5..5821e76c6b 100644 --- a/test/client/Query.test.tsx +++ b/test/client/Query.test.tsx @@ -982,4 +982,105 @@ describe('Query component', () => { , ); }); + + it('should be able to refetch after there was a network error', done => { + const query = gql` + query somethingelse { + allPeople(first: 1) { + people { + name + } + } + } + `; + + const data = { allPeople: { people: [{ name: 'Luke Skywalker' }] } }; + const dataTwo = { allPeople: { people: [{ name: 'Princess Leia' }] } }; + const link = mockSingleLink( + { request: { query }, result: { data } }, + { request: { query }, error: new Error('This is an error!') }, + { request: { query }, result: { data: dataTwo } }, + ); + const client = new ApolloClient({ + link, + cache: new Cache({ addTypename: false }), + }); + + let count = 0; + const noop = () => null; + + function Container() { + return ( + + {(result) => { + try { + switch (count++) { + case 0: + // Waiting for the first result to load + expect(result.loading).toBeTruthy(); + break; + case 1: + // First result is loaded, run a refetch to get the second result + // which is an error. + expect(stripSymbols(result.data.allPeople)).toEqual( + data.allPeople, + ); + setTimeout(() => { + result + .refetch() + .then((val) => { + done.fail('Expected error value on first refetch.'); + }, noop); + }, 0); + break; + case 2: + // Waiting for the second result to load + expect(result.loading).toBeTruthy(); + break; + case 3: + // The error arrived, run a refetch to get the third result + // which should now contain valid data. + expect(result.loading).toBeFalsy(); + expect(result.error).toBeTruthy(); + setTimeout(() => { + result + .refetch() + .catch(() => { + done.fail('Expected good data on second refetch.'); + }); + }, 0); + break; + // Further fix required in QueryManager, we should have an extra + // step for the loading status of the third result + // case 4: + // expect(result.loading).toBeTruthy(); + // expect(result.error).toBeFalsy(); + // break; + case 4: + // Third result's data is loaded + expect(result.loading).toBeFalsy(); + expect(result.error).toBeFalsy(); + expect(stripSymbols(result.data.allPeople)).toEqual( + dataTwo.allPeople, + ); + done(); + break; + default: + throw new Error('Unexpected fall through'); + } + } catch (e) { + done.fail(e); + } + return null; + }} + + ) + } + + wrapper = mount( + + + , + ); + }); }); diff --git a/test/client/__snapshots__/Query.test.tsx.snap b/test/client/__snapshots__/Query.test.tsx.snap index b43d690f45..c19176c824 100644 --- a/test/client/__snapshots__/Query.test.tsx.snap +++ b/test/client/__snapshots__/Query.test.tsx.snap @@ -25,9 +25,7 @@ Object { } `; -exports[ - `Query component calls the children prop: result in render prop while loading 1` -] = ` +exports[`Query component calls the children prop: result in render prop while loading 1`] = ` Object { "data": Object {}, "error": undefined,