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 });