From e3d611daf7a014e5c92d6bed75d67b9187437eda Mon Sep 17 00:00:00 2001
From: Cayla Hamann <38332422+caylahamann@users.noreply.github.com>
Date: Thu, 27 Jul 2023 19:43:45 -0400
Subject: [PATCH] fix: useMutation calls onError when errorPolicy is all
(#11103)
---
.changeset/early-pumpkins-type.md | 5 ++
.size-limit.cjs | 2 +-
.../hooks/__tests__/useMutation.test.tsx | 82 +++++++++++++++++++
src/react/hooks/useMutation.ts | 11 ++-
4 files changed, 98 insertions(+), 2 deletions(-)
create mode 100644 .changeset/early-pumpkins-type.md
diff --git a/.changeset/early-pumpkins-type.md b/.changeset/early-pumpkins-type.md
new file mode 100644
index 00000000000..d706a6d1f68
--- /dev/null
+++ b/.changeset/early-pumpkins-type.md
@@ -0,0 +1,5 @@
+---
+"@apollo/client": patch
+---
+
+Fixes a bug in `useMutation` so that `onError` is called when an error is returned from the request with `errorPolicy` set to 'all' .
diff --git a/.size-limit.cjs b/.size-limit.cjs
index 9de3cde6686..54cacceb903 100644
--- a/.size-limit.cjs
+++ b/.size-limit.cjs
@@ -1,7 +1,7 @@
const checks = [
{
path: "dist/apollo-client.min.cjs",
- limit: "38000"
+ limit: "38042"
},
{
path: "dist/main.cjs",
diff --git a/src/react/hooks/__tests__/useMutation.test.tsx b/src/react/hooks/__tests__/useMutation.test.tsx
index c3167a60164..63d73194b09 100644
--- a/src/react/hooks/__tests__/useMutation.test.tsx
+++ b/src/react/hooks/__tests__/useMutation.test.tsx
@@ -437,6 +437,50 @@ describe('useMutation Hook', () => {
expect(fetchResult.errors[0].message).toEqual(CREATE_TODO_ERROR);
});
+ it(`should call onError when errorPolicy is 'all'`, async () => {
+ const variables = {
+ description: 'Get milk!'
+ };
+
+ const mocks = [
+ {
+ request: {
+ query: CREATE_TODO_MUTATION,
+ variables
+ },
+ result: {
+ data: CREATE_TODO_RESULT,
+ errors: [new GraphQLError(CREATE_TODO_ERROR)],
+ },
+ }
+ ];
+
+ const onError = jest.fn();
+ const onCompleted = jest.fn();
+
+ const { result } = renderHook(
+ () => useMutation(CREATE_TODO_MUTATION, { errorPolicy: 'all', onError, onCompleted }),
+ { wrapper: ({ children }) => (
+
+ {children}
+
+ )},
+ );
+
+ const createTodo = result.current[0];
+
+ let fetchResult: any;
+ await act(async () => {
+ fetchResult = await createTodo({ variables });
+ });
+
+ expect(fetchResult.data).toEqual(CREATE_TODO_RESULT);
+ expect(fetchResult.errors[0].message).toEqual(CREATE_TODO_ERROR);
+ expect(onError).toHaveBeenCalledTimes(1);
+ expect(onError.mock.calls[0][0].message).toBe(CREATE_TODO_ERROR);
+ expect(onCompleted).not.toHaveBeenCalled();
+ });
+
it(`should ignore errors when errorPolicy is 'ignore'`, async () => {
const errorMock = jest.spyOn(console, "error")
.mockImplementation(() => {});
@@ -476,6 +520,44 @@ describe('useMutation Hook', () => {
expect(errorMock.mock.calls[0][0]).toMatch("Missing field");
errorMock.mockRestore();
});
+
+ it(`should not call onError when errorPolicy is 'ignore'`, async () => {
+ const variables = {
+ description: 'Get milk!'
+ };
+
+ const mocks = [
+ {
+ request: {
+ query: CREATE_TODO_MUTATION,
+ variables,
+ },
+ result: {
+ errors: [new GraphQLError(CREATE_TODO_ERROR)],
+ },
+ }
+ ];
+
+ const onError = jest.fn();
+
+ const { result } = renderHook(
+ () => useMutation(CREATE_TODO_MUTATION, { errorPolicy: "ignore", onError }),
+ { wrapper: ({ children }) => (
+
+ {children}
+
+ )},
+ );
+
+ const createTodo = result.current[0];
+ let fetchResult: any;
+ await act(async () => {
+ fetchResult = await createTodo({ variables });
+ });
+
+ expect(fetchResult).toEqual({});
+ expect(onError).not.toHaveBeenCalled();
+ });
});
it('should return the current client instance in the result object', async () => {
diff --git a/src/react/hooks/useMutation.ts b/src/react/hooks/useMutation.ts
index 813db9b5a7e..f2ecd587b7e 100644
--- a/src/react/hooks/useMutation.ts
+++ b/src/react/hooks/useMutation.ts
@@ -88,6 +88,12 @@ export function useMutation<
? new ApolloError({ graphQLErrors: errors })
: void 0;
+ const onError = executeOptions.onError || ref.current.options?.onError
+
+ if (error && onError) {
+ onError(error, clientOptions);
+ }
+
if (
mutationId === ref.current.mutationId &&
!clientOptions.ignoreResults
@@ -106,7 +112,10 @@ export function useMutation<
}
const onCompleted = executeOptions.onCompleted || ref.current.options?.onCompleted
- onCompleted?.(response.data!, clientOptions);
+
+ if (!error) {
+ onCompleted?.(response.data!, clientOptions);
+ }
return response;
}).catch((error) => {