Repository: apollographql/apollo-client

Problem statement:
useLazyQuery ignores changes to the query document
### Issue Description

Despite what the documentation for useLazyQuery appears to suggest. Updating the "Query" option inside the trigger of the lazyQuery does nothing.

i.e. 
`const [fetchMyQuery] = useLazyQuery(QUERY_1)`

and later calling `fetchMyQuery({ query: SOME_OTHER_QUERY})` still calls QUERY_1

### Link to Reproduction

https://codesandbox.io/embed/great-mestorf-f9nmze?fontsize=14&hidenavigation=1&theme=dark

### Reproduction Steps

useLazyQuery with a default gql query,

Override it

The original query is executed and changes are ignored.


Hints:
Hey @laytong 👋 

This looks related to #10198, just used in a slightly different way. Seeing as `useQuery` supports the ability to [swap queries](https://github.com/apollographql/apollo-client/blob/b4a4bd16a911a233de6ada47780c84512f885401/src/react/hooks/__tests__/useQuery.test.tsx#L775-L810), I see no reason this shouldn't either. 

I can't guarantee a time frame on a fix, but we will try to get to this when we can! Feel free to submit a PR if you're interested in fixing yourself and we'd be happy to review it 🙂 



Patch:
diff --git a/config/bundlesize.ts b/config/bundlesize.ts
--- a/config/bundlesize.ts
+++ b/config/bundlesize.ts
@@ -3,7 +3,7 @@ import { join } from "path";
 import { gzipSync } from "zlib";
 import bytes from "bytes";
 
-const gzipBundleByteLengthLimit = bytes("32.37KB");
+const gzipBundleByteLengthLimit = bytes("32.42KB");
 const minFile = join("dist", "apollo-client.min.cjs");
 const minPath = join(__dirname, "..", minFile);
 const gzipByteLen = gzipSync(readFileSync(minPath)).byteLength;
diff --git a/src/react/hooks/useLazyQuery.ts b/src/react/hooks/useLazyQuery.ts
--- a/src/react/hooks/useLazyQuery.ts
+++ b/src/react/hooks/useLazyQuery.ts
@@ -28,15 +28,14 @@ export function useLazyQuery<TData = any, TVariables extends OperationVariables
   options?: LazyQueryHookOptions<TData, TVariables>
 ): LazyQueryResultTuple<TData, TVariables> {
   const abortControllersRef = useRef(new Set<AbortController>());
-  const internalState = useInternalState(
-    useApolloClient(options && options.client),
-    query,
-  );
 
   const execOptionsRef = useRef<Partial<LazyQueryHookOptions<TData, TVariables>>>();
-  const merged = execOptionsRef.current
-    ? mergeOptions(options, execOptionsRef.current)
-    : options;
+  const merged = execOptionsRef.current ? mergeOptions(options, execOptionsRef.current) : options;
+
+  const internalState = useInternalState<TData, TVariables>(
+    useApolloClient(options && options.client),
+    merged?.query ?? query
+  );
 
   const useQueryResult = internalState.useQuery({
     ...merged,
diff --git a/src/utilities/common/mergeOptions.ts b/src/utilities/common/mergeOptions.ts
--- a/src/utilities/common/mergeOptions.ts
+++ b/src/utilities/common/mergeOptions.ts
@@ -13,7 +13,7 @@ type OptionsUnion<TData, TVariables extends OperationVariables, TContext> =
   | MutationOptions<TData, TVariables, TContext>;
 
 export function mergeOptions<
-  TOptions extends OptionsUnion<any, any, any>
+  TOptions extends Partial<OptionsUnion<any, any, any>>
 >(
   defaults: TOptions | Partial<TOptions> | undefined,
   options: TOptions | Partial<TOptions>,


Test patch:
diff --git a/src/react/hooks/__tests__/useLazyQuery.test.tsx b/src/react/hooks/__tests__/useLazyQuery.test.tsx
--- a/src/react/hooks/__tests__/useLazyQuery.test.tsx
+++ b/src/react/hooks/__tests__/useLazyQuery.test.tsx
@@ -439,6 +439,77 @@ describe('useLazyQuery Hook', () => {
     });
   });
 
+
+  it("changing queries", async () => {
+    const query1 = gql`
+      query {
+        hello
+      }
+    `;
+    const query2 = gql`
+      query {
+        name
+      }
+    `;
+    const mocks = [
+      {
+        request: { query: query1 },
+        result: { data: { hello: "world" } },
+      },
+      {
+        request: { query: query2 },
+        result: { data: { name: "changed" } },
+      },
+    ];
+
+    const cache = new InMemoryCache();
+    const { result } = renderHook(() => useLazyQuery(query1), {
+      wrapper: ({ children }) => (
+        <MockedProvider mocks={mocks} cache={cache}>
+          {children}
+        </MockedProvider>
+      ),
+    });
+
+    expect(result.current[1].loading).toBe(false);
+    expect(result.current[1].data).toBe(undefined);
+    const execute = result.current[0];
+
+    setTimeout(() => execute());
+
+    await waitFor(
+      () => {
+        expect(result.current[1].loading).toBe(true);
+      },
+      { interval: 1 }
+    );
+
+    await waitFor(
+      () => {
+        expect(result.current[1].loading).toBe(false);
+      },
+      { interval: 1 }
+    );
+    expect(result.current[1].data).toEqual({ hello: "world" });
+
+    setTimeout(() => execute({ query: query2 }));
+
+    await waitFor(
+      () => {
+        expect(result.current[1].loading).toBe(true);
+      },
+      { interval: 1 }
+    );
+
+    await waitFor(
+      () => {
+        expect(result.current[1].loading).toBe(false);
+      },
+      { interval: 1 }
+    );
+    expect(result.current[1].data).toEqual({ name: "changed" });
+  });
+
   it('should fetch data each time the execution function is called, when using a "network-only" fetch policy', async () => {
     const mocks = [
       {