diff --git a/.changeset/short-bikes-mate.md b/.changeset/short-bikes-mate.md new file mode 100644 index 00000000000..fab5b4f20cb --- /dev/null +++ b/.changeset/short-bikes-mate.md @@ -0,0 +1,5 @@ +--- +'@apollo/client': patch +--- + +Always throw network errors in `useSuspenseQuery` regardless of the set `errorPolicy`. diff --git a/src/react/hooks/__tests__/useSuspenseQuery.test.tsx b/src/react/hooks/__tests__/useSuspenseQuery.test.tsx index 14e13c2d331..069f69387ff 100644 --- a/src/react/hooks/__tests__/useSuspenseQuery.test.tsx +++ b/src/react/hooks/__tests__/useSuspenseQuery.test.tsx @@ -2097,30 +2097,32 @@ describe('useSuspenseQuery', () => { consoleSpy.mockRestore(); }); - it('does not throw or return network errors when errorPolicy is set to "ignore"', async () => { - const { query, mocks } = useErrorCase({ - networkError: new Error('Could not fetch'), - }); + it('throws network errors when errorPolicy is set to "ignore"', async () => { + const consoleSpy = jest.spyOn(console, 'error').mockImplementation(); + const networkError = new Error('Could not fetch'); - const { result, renders } = renderSuspenseHook( + const { query, mocks } = useErrorCase({ networkError }); + + const { renders } = renderSuspenseHook( () => useSuspenseQuery(query, { errorPolicy: 'ignore' }), { mocks } ); await waitFor(() => { - expect(result.current).toMatchObject({ - data: undefined, - error: undefined, - }); + expect(renders.errorCount).toBe(1); }); - expect(renders.errorCount).toBe(0); - expect(renders.errors).toEqual([]); - expect(renders.count).toBe(2); + expect(renders.errors.length).toBe(1); expect(renders.suspenseCount).toBe(1); - expect(renders.frames).toMatchObject([ - { data: undefined, error: undefined }, - ]); + expect(renders.frames).toEqual([]); + + const [error] = renders.errors as ApolloError[]; + + expect(error).toBeInstanceOf(ApolloError); + expect(error!.networkError).toEqual(networkError); + expect(error!.graphQLErrors).toEqual([]); + + consoleSpy.mockRestore(); }); it('does not throw or return graphql errors when errorPolicy is set to "ignore"', async () => { @@ -2149,7 +2151,7 @@ describe('useSuspenseQuery', () => { ]); }); - it('returns partial data results and discards errors when errorPolicy is set to "ignore"', async () => { + it('returns partial data results and discards GraphQL errors when errorPolicy is set to "ignore"', async () => { const { query, mocks } = useErrorCase({ data: { currentUser: { id: '1', name: null } }, graphQLErrors: [new GraphQLError('`name` could not be found')], @@ -2200,36 +2202,33 @@ describe('useSuspenseQuery', () => { ]); }); - it('does not throw and returns network errors when errorPolicy is set to "all"', async () => { + it('throws network errors when errorPolicy is set to "all"', async () => { + const consoleSpy = jest.spyOn(console, 'error').mockImplementation(); + const networkError = new Error('Could not fetch'); const { query, mocks } = useErrorCase({ networkError }); - const { result, renders } = renderSuspenseHook( + const { renders } = renderSuspenseHook( () => useSuspenseQuery(query, { errorPolicy: 'all' }), { mocks } ); await waitFor(() => { - expect(result.current).toMatchObject({ - data: undefined, - error: new ApolloError({ networkError }), - }); + expect(renders.errorCount).toBe(1); }); - expect(renders.errorCount).toBe(0); - expect(renders.errors).toEqual([]); - expect(renders.count).toBe(2); + expect(renders.errors.length).toBe(1); expect(renders.suspenseCount).toBe(1); - expect(renders.frames).toMatchObject([ - { data: undefined, error: new ApolloError({ networkError }) }, - ]); + expect(renders.frames).toEqual([]); - const { error } = result.current; + const [error] = renders.errors as ApolloError[]; expect(error).toBeInstanceOf(ApolloError); expect(error!.networkError).toEqual(networkError); expect(error!.graphQLErrors).toEqual([]); + + consoleSpy.mockRestore(); }); it('does not throw and returns graphql errors when errorPolicy is set to "all"', async () => { diff --git a/src/react/hooks/useSuspenseQuery.ts b/src/react/hooks/useSuspenseQuery.ts index 3eb12e254c2..650ac3768a8 100644 --- a/src/react/hooks/useSuspenseQuery.ts +++ b/src/react/hooks/useSuspenseQuery.ts @@ -83,14 +83,17 @@ export function useSuspenseQuery_experimental< const hasPartialResult = result.data && result.partial; const usePartialResult = returnPartialData && hasPartialResult; - if ( - result.error && - errorPolicy === 'none' && + const allowsThrownErrors = // If we've got a deferred query that errors on an incremental chunk, we // will have a partial result before the error is collected. We do not want // to throw errors that have been returned from incremental chunks. Instead // we offload those errors to the `error` property. - (!deferred || !hasPartialResult) + errorPolicy === 'none' && (!deferred || !hasPartialResult); + + if ( + result.error && + // Always throw network errors regardless of the error policy + (result.error.networkError || allowsThrownErrors) ) { throw result.error; }