diff --git a/package.json b/package.json index fd74625410..ed5da8129b 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "bundlesize": [ { "path": "./lib/umd/react-apollo.js", - "maxSize": "4.7 KB" + "maxSize": "4.8 KB" } ], "lint-staged": { diff --git a/src/graphql.tsx b/src/graphql.tsx index b592eaed62..d5a7009479 100644 --- a/src/graphql.tsx +++ b/src/graphql.tsx @@ -451,6 +451,7 @@ export default function graphql< }; const handleError = error => { + this.resubscribeToQuery(); // Quick fix for https://github.com/apollostack/react-apollo/issues/378 if (error.hasOwnProperty('graphQLErrors')) return next({ error }); throw error; @@ -476,6 +477,24 @@ export default function graphql< } } + resubscribeToQuery() { + const lastSubscription = this.querySubscription; + if (lastSubscription) { + delete this.querySubscription; + } + const { lastError, lastResult } = this.queryObservable; + // 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.subscribeToQuery(); + Object.assign(this.queryObservable, { lastError, lastResult }); + if (lastSubscription) { + (lastSubscription as ZenObservable.Subscription).unsubscribe(); + } + } + shouldSkip(props = this.props) { return mapPropsToSkip(props); } diff --git a/test/client/graphql/queries/errors.test.tsx b/test/client/graphql/queries/errors.test.tsx index d305c563c5..ef74d02cb4 100644 --- a/test/client/graphql/queries/errors.test.tsx +++ b/test/client/graphql/queries/errors.test.tsx @@ -383,7 +383,7 @@ describe('[queries] errors', () => { expect(stripSymbols(props.data.allPeople)).toEqual( data.allPeople, ); - props.data.refetch(); + props.data.refetch().catch(() => null); break; case 1: expect(props.data.loading).toBeTruthy(); @@ -418,4 +418,87 @@ describe('[queries] errors', () => { , ); }); + + it('can 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; + @graphql(query, { options: { notifyOnNetworkStatusChange: true } }) + class Container extends React.Component { + componentWillReceiveProps(props) { + try { + switch (count++) { + case 0: + props.data + .refetch() + .then(() => { + done.fail('Expected error value on first refetch.'); + }) + .catch(noop); + break; + case 1: + expect(props.data.loading).toBeTruthy(); + break; + case 2: + expect(props.data.loading).toBeFalsy(); + expect(props.data.error).toBeTruthy(); + props.data + .refetch() + .then(noop) + .catch(() => { + done.fail('Expected good data on second refetch.'); + }); + break; + // Further fix required in QueryManager + // case 3: + // expect(props.data.loading).toBeTruthy(); + // expect(props.data.error).toBeFalsy(); + // break; + case 3: + expect(props.data.loading).toBeFalsy(); + expect(props.data.error).toBeFalsy(); + expect(stripSymbols(props.data.allPeople)).toEqual( + dataTwo.allPeople, + ); + done(); + break; + default: + throw new Error('Unexpected fall through'); + } + } catch (e) { + done.fail(e); + } + } + + render() { + return null; + } + } + + renderer.create( + + + , + ); + }); });