diff --git a/CHANGELOG.md b/CHANGELOG.md
index 217ac8bb939..e0ae64ca600 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,6 @@
+## Apollo Client 3.5.2 (unreleased)
+- Fix useMutation execute function returning non-identical execution functions when passing similar options.
[@brainkim](https://github.com/brainkim) in [#9093](https://github.com/apollographql/apollo-client/pull/9037)
+
## Apollo Client 3.5.1 (2021-11-09)
- Remove npm from dependencies, and avoid referencing graphql-js enum value.
diff --git a/docs/package-lock.json b/docs/package-lock.json
index 62b6df94830..ab270165c4b 100644
--- a/docs/package-lock.json
+++ b/docs/package-lock.json
@@ -9934,9 +9934,9 @@
}
},
"gatsby-theme-apollo-docs": {
- "version": "5.3.2",
- "resolved": "https://registry.npmjs.org/gatsby-theme-apollo-docs/-/gatsby-theme-apollo-docs-5.3.2.tgz",
- "integrity": "sha512-qhivhgrLUD9OeUvS9qKIrOypkS/v9rBSFfDF+hQpOd0OkIOnWDuq8R9WHDmB84M1E9wLiPjaIBY2jbaWB+B98w==",
+ "version": "5.3.3",
+ "resolved": "https://registry.npmjs.org/gatsby-theme-apollo-docs/-/gatsby-theme-apollo-docs-5.3.3.tgz",
+ "integrity": "sha512-8foOpT+lEfdAk56Mj+NxmtAukF0o3Q9dyVfB1fe4w2DAPGDImTM3xUCdKtACs8SEPs/uNaHA6bY7Ed5rFPEi6g==",
"requires": {
"@jlengstorf/get-share-image": "^0.8.0",
"@mdx-js/mdx": "^1.1.0",
@@ -19626,9 +19626,9 @@
"integrity": "sha512-vJhccZPs965sV/L2sU4oRQVAos0pQXwsvTLkWYdqJ+a8Q5kPFzJTuOFwy7UniPli44NKQGAglksjvOcpo95aZA=="
},
"tippy.js": {
- "version": "6.3.5",
- "resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-6.3.5.tgz",
- "integrity": "sha512-B9hAQ5KNF+jDJRg6cRysV6Y3J+5fiNfD60GuXR5TP0sfrcltpgdzVc7f1wMtjQ3W0+Xsy80CDvk0Z+Vr0cM4sQ==",
+ "version": "6.3.6",
+ "resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-6.3.6.tgz",
+ "integrity": "sha512-59OpvnA4jxYxSTCNu7mb3gUaCrnuyi34FbWxAf9gG4ApJKn4NrJdHO2exB/5TOWf9zl6v03hluRVtZFVYrm/bQ==",
"requires": {
"@popperjs/core": "^2.9.0"
}
diff --git a/docs/package.json b/docs/package.json
index 0dc7eb5dcf8..3e89548722b 100644
--- a/docs/package.json
+++ b/docs/package.json
@@ -8,7 +8,7 @@
},
"dependencies": {
"gatsby": "2.32.13",
- "gatsby-theme-apollo-docs": "5.3.2",
+ "gatsby-theme-apollo-docs": "5.3.3",
"react": "17.0.2",
"react-dom": "17.0.1",
"webpack-virtual-modules": "0.4.3"
diff --git a/package.json b/package.json
index d1c187c76d4..b87ff25a718 100644
--- a/package.json
+++ b/package.json
@@ -55,7 +55,7 @@
{
"name": "apollo-client",
"path": "./dist/apollo-client.min.cjs",
- "maxSize": "28.45 kB"
+ "maxSize": "28.5kB"
}
],
"engines": {
diff --git a/src/react/hooks/__tests__/useMutation.test.tsx b/src/react/hooks/__tests__/useMutation.test.tsx
index 1ed4eccc2a0..ce1de434f7c 100644
--- a/src/react/hooks/__tests__/useMutation.test.tsx
+++ b/src/react/hooks/__tests__/useMutation.test.tsx
@@ -135,7 +135,7 @@ describe('useMutation Hook', () => {
];
const { result, waitForNextUpdate } = renderHook(
- () => useMutation(CREATE_TODO_MUTATION),
+ () => useMutation(CREATE_TODO_MUTATION, {}),
{ wrapper: ({ children }) => (
{children}
diff --git a/src/react/hooks/useMutation.ts b/src/react/hooks/useMutation.ts
index 5885d3aaf3f..b35206dee7e 100644
--- a/src/react/hooks/useMutation.ts
+++ b/src/react/hooks/useMutation.ts
@@ -1,4 +1,4 @@
-import { useCallback, useEffect, useRef, useState } from 'react';
+import { useCallback, useMemo, useEffect, useRef, useState } from 'react';
import { DocumentNode } from 'graphql';
import { TypedDocumentNode } from '@graphql-typed-document-node/core';
import {
@@ -40,87 +40,105 @@ export function useMutation<
result,
mutationId: 0,
isMounted: true,
+ execute: null as null | MutationTuple[0],
+ client,
+ mutation,
+ options,
});
- const execute = useCallback((
- executeOptions: MutationFunctionOptions<
- TData,
- TVariables,
- TContext,
- TCache
- > = {},
- ) => {
-
- const baseOptions = { ...options, mutation };
- if (!ref.current.result.loading && !baseOptions.ignoreResults) {
- setResult(ref.current.result = {
- loading: true,
- error: void 0,
- data: void 0,
- called: true,
- client,
- });
+ const execute = useMemo(() => {
+ if (
+ ref.current.execute != null &&
+ ref.current.client === client &&
+ equal(options, ref.current.options) &&
+ equal(mutation, ref.current.mutation)) {
+ return ref.current.execute;
}
- const mutationId = ++ref.current.mutationId;
- const clientOptions = mergeOptions(
- baseOptions,
- executeOptions as any,
- );
-
- return client.mutate(clientOptions).then((response) =>{
- const { data, errors } = response;
- const error =
- errors && errors.length > 0
- ? new ApolloError({ graphQLErrors: errors })
- : void 0;
-
- if (
- mutationId === ref.current.mutationId &&
- !baseOptions.ignoreResults
- ) {
- const result = {
+ ref.current.client = client;
+ ref.current.options = options;
+ ref.current.mutation = mutation;
+ ref.current.execute = (
+ executeOptions: MutationFunctionOptions<
+ TData,
+ TVariables,
+ TContext,
+ TCache
+ > = {},
+ ) => {
+ const baseOptions = { ...options, mutation };
+ if (!ref.current.result.loading && !baseOptions.ignoreResults) {
+ setResult(ref.current.result = {
+ loading: true,
+ error: void 0,
+ data: void 0,
called: true,
- loading: false,
- data,
- error,
client,
- };
+ });
+ }
- if (ref.current.isMounted && !equal(ref.current.result, result)) {
- setResult(ref.current.result = result);
+ const mutationId = ++ref.current.mutationId;
+ const clientOptions = mergeOptions(
+ baseOptions,
+ executeOptions as any,
+ );
+
+ return client.mutate(clientOptions).then((response) =>{
+ const { data, errors } = response;
+ const error =
+ errors && errors.length > 0
+ ? new ApolloError({ graphQLErrors: errors })
+ : void 0;
+
+ if (
+ mutationId === ref.current.mutationId &&
+ !baseOptions.ignoreResults
+ ) {
+ const result = {
+ called: true,
+ loading: false,
+ data,
+ error,
+ client,
+ };
+
+ if (ref.current.isMounted && !equal(ref.current.result, result)) {
+ setResult(ref.current.result = result);
+ }
}
- }
- baseOptions.onCompleted?.(response.data!);
- return response;
- }).catch((error) => {
- if (
- mutationId === ref.current.mutationId &&
- ref.current.isMounted
- ) {
- const result = {
- loading: false,
- error,
- data: void 0,
- called: true,
- client,
- };
+ baseOptions.onCompleted?.(response.data!);
+ return response;
+ }).catch((error) => {
+ if (
+ mutationId === ref.current.mutationId &&
+ ref.current.isMounted
+ ) {
+ const result = {
+ loading: false,
+ error,
+ data: void 0,
+ called: true,
+ client,
+ };
+
+ if (!equal(ref.current.result, result)) {
+ setResult(ref.current.result = result);
+ }
+ }
- if (!equal(ref.current.result, result)) {
- setResult(ref.current.result = result);
+ if (baseOptions.onError) {
+ baseOptions.onError(error);
+ // TODO(brian): why are we returning this here???
+ return { data: void 0, errors: error };
}
- }
- if (baseOptions.onError) {
- baseOptions.onError(error);
- // TODO(brian): why are we returning this here???
- return { data: void 0, errors: error };
- }
+ throw error;
+ });
+ };
- throw error;
- });
- }, [client, options, mutation]);
+ return ref.current.execute;
+ }, [client, mutation, options]);
const reset = useCallback(() => {
setResult({ called: false, loading: false, client });