From d108135b845dc013945b8c9ab3be4e40208d0319 Mon Sep 17 00:00:00 2001 From: Brian Kim Date: Tue, 9 Nov 2021 19:55:56 -0500 Subject: [PATCH] fix useMutation hook returning different execute functions --- src/react/hooks/useMutation.ts | 156 ++++++++++++++++++--------------- 1 file changed, 87 insertions(+), 69 deletions(-) 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 });