Repository: urql-graphql/urql
Pull request #3598 (2024-06-01 10:13:32)

fix(vue): pause not working with getters
## Summary

This PR fixes a bug noted by @negezor: the `pause` property was not working with a getter, see https://github.com/urql-graphql/urql/pull/3595#issuecomment-2142285519.

## Set of changes

1) Minor fixes in `useQuery.ts` and `useSubscription.ts` to manage the case where pause is a getter.

2) Corrected some typos in the docs of `useQuery.ts` and `useSubscription.ts`.

3) Modified a test in `useQuery.test.ts` to use a reactive `pause` property with a getter.


changeset-bot[bot]: ###  🦋  Changeset detected

Latest commit: be6c80bf1822d460bbb0d28a508c45d921253452

**The changes in this PR will be included in the next version bump.**

<details><summary>This PR includes changesets to release 1 package</summary>

  | Name      | Type  |
| --------- | ----- |
| @urql/vue | Patch |

</details>

Not sure what this means? [Click here  to learn what changesets are](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add another changeset to this PR](https://github.com/arkandias/urql/new/main?filename=.changeset/big-trainers-turn.md&value=---%0A%22%40urql%2Fvue%22%3A%20patch%0A---%0A%0AUpdate%20to%20Vue%203.4%20and%20fix%20pause%20not%20working%20with%20getters%0A)



Patch:
diff --git a/.changeset/fluffy-seals-confess.md b/.changeset/fluffy-seals-confess.md
new file mode 100644
index 00000000..7215672d
--- /dev/null
+++ b/.changeset/fluffy-seals-confess.md
@@ -0,0 +1,5 @@
+---
+'@urql/vue': patch
+---
+
+Fix reactaive typings for `variables` (See [#3605](https://github.com/urql-graphql/urql/pull/3605))
diff --git a/.changeset/friendly-timers-taste.md b/.changeset/friendly-timers-taste.md
new file mode 100644
index 00000000..7d6b8aa0
--- /dev/null
+++ b/.changeset/friendly-timers-taste.md
@@ -0,0 +1,5 @@
+---
+'@urql/vue': patch
+---
+
+Restore the possibility to use a getter for the pause property.
diff --git a/examples/with-apq/package.json b/examples/with-apq/package.json
index 336e981f..b5869959 100644
--- a/examples/with-apq/package.json
+++ b/examples/with-apq/package.json
@@ -6,7 +6,7 @@
     "start": "vite"
   },
   "dependencies": {
-    "@urql/core": "^5.0.3",
+    "@urql/core": "^5.0.4",
     "@urql/exchange-persisted": "^4.3.0",
     "graphql": "^16.6.0",
     "react": "^18.2.0",
diff --git a/examples/with-defer-stream-directives/package.json b/examples/with-defer-stream-directives/package.json
index 2ccdb16f..24361663 100644
--- a/examples/with-defer-stream-directives/package.json
+++ b/examples/with-defer-stream-directives/package.json
@@ -17,8 +17,8 @@
   },
   "dependencies": {
     "@graphql-yoga/plugin-defer-stream": "^1.7.1",
-    "@urql/core": "^5.0.3",
-    "@urql/exchange-graphcache": "^7.1.0",
+    "@urql/core": "^5.0.4",
+    "@urql/exchange-graphcache": "^7.1.1",
     "graphql": "17.0.0-alpha.2",
     "react": "^18.2.0",
     "react-dom": "^18.2.0",
diff --git a/examples/with-graphcache-pagination/package.json b/examples/with-graphcache-pagination/package.json
index 8e982d0b..7298a1ad 100644
--- a/examples/with-graphcache-pagination/package.json
+++ b/examples/with-graphcache-pagination/package.json
@@ -6,8 +6,8 @@
     "start": "vite"
   },
   "dependencies": {
-    "@urql/core": "^5.0.3",
-    "@urql/exchange-graphcache": "^7.1.0",
+    "@urql/core": "^5.0.4",
+    "@urql/exchange-graphcache": "^7.1.1",
     "graphql": "^16.6.0",
     "react": "^18.2.0",
     "react-dom": "^18.2.0",
diff --git a/examples/with-graphcache-updates/package.json b/examples/with-graphcache-updates/package.json
index fe15c24c..81089d76 100644
--- a/examples/with-graphcache-updates/package.json
+++ b/examples/with-graphcache-updates/package.json
@@ -6,9 +6,9 @@
     "start": "vite"
   },
   "dependencies": {
-    "@urql/core": "^5.0.3",
+    "@urql/core": "^5.0.4",
     "@urql/exchange-auth": "^2.2.0",
-    "@urql/exchange-graphcache": "^7.1.0",
+    "@urql/exchange-graphcache": "^7.1.1",
     "graphql": "^16.6.0",
     "react": "^18.2.0",
     "react-dom": "^18.2.0",
diff --git a/examples/with-infinite-pagination/package.json b/examples/with-infinite-pagination/package.json
index 0dcf0049..daac894a 100644
--- a/examples/with-infinite-pagination/package.json
+++ b/examples/with-infinite-pagination/package.json
@@ -6,7 +6,7 @@
     "start": "vite"
   },
   "dependencies": {
-    "@urql/core": "^5.0.3",
+    "@urql/core": "^5.0.4",
     "graphql": "^16.6.0",
     "react": "^18.2.0",
     "react-dom": "^18.2.0",
diff --git a/examples/with-multipart/package.json b/examples/with-multipart/package.json
index 1c11b0f9..e7b9f3e5 100644
--- a/examples/with-multipart/package.json
+++ b/examples/with-multipart/package.json
@@ -6,7 +6,7 @@
     "start": "vite"
   },
   "dependencies": {
-    "@urql/core": "^5.0.3",
+    "@urql/core": "^5.0.4",
     "graphql": "^16.6.0",
     "react": "^18.2.0",
     "react-dom": "^18.2.0",
diff --git a/examples/with-next/package.json b/examples/with-next/package.json
index e1b545d4..a78661df 100644
--- a/examples/with-next/package.json
+++ b/examples/with-next/package.json
@@ -3,7 +3,7 @@
   "version": "0.0.0",
   "private": true,
   "dependencies": {
-    "@urql/core": "^5.0.3",
+    "@urql/core": "^5.0.4",
     "@urql/next": "^1.1.1",
     "graphql": "^16.6.0",
     "next": "13.4.2",
diff --git a/examples/with-pagination/package.json b/examples/with-pagination/package.json
index 0dcf0049..daac894a 100644
--- a/examples/with-pagination/package.json
+++ b/examples/with-pagination/package.json
@@ -6,7 +6,7 @@
     "start": "vite"
   },
   "dependencies": {
-    "@urql/core": "^5.0.3",
+    "@urql/core": "^5.0.4",
     "graphql": "^16.6.0",
     "react": "^18.2.0",
     "react-dom": "^18.2.0",
diff --git a/examples/with-react-native/package.json b/examples/with-react-native/package.json
index 6f2d267e..b3a1ab52 100644
--- a/examples/with-react-native/package.json
+++ b/examples/with-react-native/package.json
@@ -8,7 +8,7 @@
     "start": "react-native start"
   },
   "dependencies": {
-    "@urql/core": "^5.0.3",
+    "@urql/core": "^5.0.4",
     "graphql": "^16.6.0",
     "react": "18.2.0",
     "react-native": "0.71.4",
diff --git a/examples/with-react/package.json b/examples/with-react/package.json
index 36ff7257..8073e3c9 100644
--- a/examples/with-react/package.json
+++ b/examples/with-react/package.json
@@ -6,7 +6,7 @@
     "start": "vite"
   },
   "dependencies": {
-    "@urql/core": "^5.0.3",
+    "@urql/core": "^5.0.4",
     "graphql": "^16.6.0",
     "react": "^18.2.0",
     "react-dom": "^18.2.0",
diff --git a/examples/with-refresh-auth/package.json b/examples/with-refresh-auth/package.json
index 0bff6269..38a1c5cb 100644
--- a/examples/with-refresh-auth/package.json
+++ b/examples/with-refresh-auth/package.json
@@ -6,7 +6,7 @@
     "start": "vite"
   },
   "dependencies": {
-    "@urql/core": "^5.0.3",
+    "@urql/core": "^5.0.4",
     "@urql/exchange-auth": "^2.2.0",
     "graphql": "^16.6.0",
     "react": "^18.2.0",
diff --git a/examples/with-retry/package.json b/examples/with-retry/package.json
index a5c5dfa3..2642010c 100644
--- a/examples/with-retry/package.json
+++ b/examples/with-retry/package.json
@@ -6,7 +6,7 @@
     "start": "vite"
   },
   "dependencies": {
-    "@urql/core": "^5.0.3",
+    "@urql/core": "^5.0.4",
     "@urql/exchange-retry": "^1.3.0",
     "graphql": "^16.6.0",
     "react": "^18.2.0",
diff --git a/examples/with-subscriptions-via-fetch/package.json b/examples/with-subscriptions-via-fetch/package.json
index e6686586..e53dede0 100644
--- a/examples/with-subscriptions-via-fetch/package.json
+++ b/examples/with-subscriptions-via-fetch/package.json
@@ -8,8 +8,8 @@
     "start": "run-p client server"
   },
   "dependencies": {
-    "@urql/core": "^5.0.3",
-    "@urql/exchange-graphcache": "^7.1.0",
+    "@urql/core": "^5.0.4",
+    "@urql/exchange-graphcache": "^7.1.1",
     "graphql": "^16.6.0",
     "react": "^18.2.0",
     "react-dom": "^18.2.0",
diff --git a/examples/with-svelte/package.json b/examples/with-svelte/package.json
index e6a20ac2..5c48c9b8 100644
--- a/examples/with-svelte/package.json
+++ b/examples/with-svelte/package.json
@@ -8,7 +8,7 @@
     "serve": "vite preview"
   },
   "dependencies": {
-    "@urql/core": "^5.0.3",
+    "@urql/core": "^5.0.4",
     "@urql/svelte": "^4.2.0",
     "graphql": "^16.6.0",
     "svelte": "^4.0.5"
diff --git a/examples/with-vue3/package.json b/examples/with-vue3/package.json
index 535a5825..b4f36bea 100644
--- a/examples/with-vue3/package.json
+++ b/examples/with-vue3/package.json
@@ -8,7 +8,7 @@
     "serve": "vite preview"
   },
   "dependencies": {
-    "@urql/core": "^5.0.3",
+    "@urql/core": "^5.0.4",
     "@urql/vue": "^1.2.1",
     "graphql": "^16.6.0",
     "vue": "^3.2.47"
diff --git a/exchanges/graphcache/CHANGELOG.md b/exchanges/graphcache/CHANGELOG.md
index 77719b72..b149eada 100644
--- a/exchanges/graphcache/CHANGELOG.md
+++ b/exchanges/graphcache/CHANGELOG.md
@@ -1,5 +1,12 @@
 # @urql/exchange-graphcache
 
+## 7.1.1
+
+### Patch Changes
+
+- ⚠️ Fix where we would incorrectly match all fragment concrete types because they belong to the abstract type
+  Submitted by [@JoviDeCroock](https://github.com/JoviDeCroock) (See [#3603](https://github.com/urql-graphql/urql/pull/3603))
+
 ## 7.1.0
 
 ### Minor Changes
diff --git a/exchanges/graphcache/jsr.json b/exchanges/graphcache/jsr.json
index 1800b19c..4949dfa6 100644
--- a/exchanges/graphcache/jsr.json
+++ b/exchanges/graphcache/jsr.json
@@ -1,6 +1,6 @@
 {
   "name": "@urql/exchange-graphcache",
-  "version": "7.1.0",
+  "version": "7.1.1",
   "exports": {
     ".": "./src/index.ts",
     "./extras": "./src/extras/index.ts",
diff --git a/exchanges/graphcache/package.json b/exchanges/graphcache/package.json
index 3b1a5783..2282cfe9 100644
--- a/exchanges/graphcache/package.json
+++ b/exchanges/graphcache/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@urql/exchange-graphcache",
-  "version": "7.1.0",
+  "version": "7.1.1",
   "description": "A normalized and configurable cache exchange for urql",
   "sideEffects": false,
   "homepage": "https://formidable.com/open-source/urql/docs/graphcache",
diff --git a/exchanges/graphcache/src/operations/shared.ts b/exchanges/graphcache/src/operations/shared.ts
index bf158c11..4dad5e09 100644
--- a/exchanges/graphcache/src/operations/shared.ts
+++ b/exchanges/graphcache/src/operations/shared.ts
@@ -23,6 +23,7 @@ import {
   currentOptimistic,
   writeConcreteType,
   getConcreteTypes,
+  isSeenConcreteType,
 } from '../store/data';
 import { keyOfField } from '../store/keys';
 import type { Store } from '../store/store';
@@ -153,14 +154,11 @@ const isFragmentHeuristicallyMatching = (
     logger
   );
 
-  return (
-    currentOperation === 'write' ||
-    !getSelectionSet(node).some(node => {
-      if (node.kind !== Kind.FIELD) return false;
-      const fieldKey = keyOfField(getName(node), getFieldArguments(node, vars));
-      return !hasField(entityKey, fieldKey);
-    })
-  );
+  return !getSelectionSet(node).some(node => {
+    if (node.kind !== Kind.FIELD) return false;
+    const fieldKey = keyOfField(getName(node), getFieldArguments(node, vars));
+    return !hasField(entityKey, fieldKey);
+  });
 };
 
 interface SelectionIterator {
@@ -238,11 +236,12 @@ export function makeSelectionIterator(
                     ctx.store.logger
                   ));
 
-            if (isMatching) {
+            if (isMatching || currentOperation === 'write') {
               if (process.env.NODE_ENV !== 'production')
                 pushDebugNode(typename, fragment);
               const isFragmentOptional = isOptional(select);
               if (
+                isMatching &&
                 fragment.typeCondition &&
                 typename !== fragment.typeCondition.name.value
               ) {
@@ -272,6 +271,10 @@ export function makeSelectionIterator(
 const isFragmentMatching = (typeCondition: string, typename: string | void) => {
   if (!typename) return false;
   if (typeCondition === typename) return true;
+
+  const isProbableAbstractType = !isSeenConcreteType(typeCondition);
+  if (!isProbableAbstractType) return false;
+
   const types = getConcreteTypes(typeCondition);
   return types.size && types.has(typename);
 };
diff --git a/exchanges/graphcache/src/store/data.ts b/exchanges/graphcache/src/store/data.ts
index 32ac1c46..20ad4c62 100644
--- a/exchanges/graphcache/src/store/data.ts
+++ b/exchanges/graphcache/src/store/data.ts
@@ -492,6 +492,9 @@ export const writeType = (typename: string, entityKey: string) => {
 export const getConcreteTypes = (typename: string): Set<string> =>
   currentData!.abstractToConcreteMap.get(typename) || DEFAULT_EMPTY_SET;
 
+export const isSeenConcreteType = (typename: string): boolean =>
+  currentData!.types.has(typename);
+
 export const writeConcreteType = (
   abstractType: string,
   concreteType: string
diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md
index fc9ba7aa..48cab2f3 100644
--- a/packages/core/CHANGELOG.md
+++ b/packages/core/CHANGELOG.md
@@ -1,5 +1,15 @@
 # @urql/core
 
+## 5.0.4
+
+### Patch Changes
+
+- Change how we calculate the `OperationKey` to take files into account, before we
+  would encode them to `null` resulting in every mutation with the same variables
+  (excluding the files) to have the same key. This resulted in mutations that upload
+  different files at the same time to share a result in GraphCache
+  Submitted by [@JoviDeCroock](https://github.com/JoviDeCroock) (See [#3601](https://github.com/urql-graphql/urql/pull/3601))
+
 ## 5.0.3
 
 ### Patch Changes
diff --git a/packages/core/jsr.json b/packages/core/jsr.json
index e999ad55..0d99437a 100644
--- a/packages/core/jsr.json
+++ b/packages/core/jsr.json
@@ -1,6 +1,6 @@
 {
   "name": "@urql/core",
-  "version": "5.0.3",
+  "version": "5.0.4",
   "exports": {
     ".": "./src/index.ts",
     "./internal": "./src/internal/index.ts"
diff --git a/packages/core/package.json b/packages/core/package.json
index 5249c861..6a16c5ab 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@urql/core",
-  "version": "5.0.3",
+  "version": "5.0.4",
   "description": "The shared core for the highly customizable and versatile GraphQL client",
   "sideEffects": false,
   "homepage": "https://formidable.com/open-source/urql/docs/",
diff --git a/packages/core/src/utils/request.ts b/packages/core/src/utils/request.ts
index aa5765b9..f0675c82 100644
--- a/packages/core/src/utils/request.ts
+++ b/packages/core/src/utils/request.ts
@@ -169,7 +169,7 @@ export const createRequest = <
 ): GraphQLRequest<Data, Variables> => {
   const variables = _variables || ({} as Variables);
   const query = keyDocument(_query);
-  const printedVars = stringifyVariables(variables);
+  const printedVars = stringifyVariables(variables, true);
   let key = query.__key;
   if (printedVars !== '{}') key = phash(printedVars, key);
   return { key, query, variables, extensions };
diff --git a/packages/core/src/utils/variables.ts b/packages/core/src/utils/variables.ts
index b1984880..fb3cf50f 100644
--- a/packages/core/src/utils/variables.ts
+++ b/packages/core/src/utils/variables.ts
@@ -3,24 +3,25 @@ export type FileMap = Map<string, File | Blob>;
 const seen: Set<any> = new Set();
 const cache: WeakMap<any, any> = new WeakMap();
 
-const stringify = (x: any): string => {
+const stringify = (x: any, includeFiles: boolean): string => {
   if (x === null || seen.has(x)) {
     return 'null';
   } else if (typeof x !== 'object') {
     return JSON.stringify(x) || '';
   } else if (x.toJSON) {
-    return stringify(x.toJSON());
+    return stringify(x.toJSON(), includeFiles);
   } else if (Array.isArray(x)) {
     let out = '[';
     for (const value of x) {
       if (out.length > 1) out += ',';
-      out += stringify(value) || 'null';
+      out += stringify(value, includeFiles) || 'null';
     }
     out += ']';
     return out;
   } else if (
-    (FileConstructor !== NoopConstructor && x instanceof FileConstructor) ||
-    (BlobConstructor !== NoopConstructor && x instanceof BlobConstructor)
+    !includeFiles &&
+    ((FileConstructor !== NoopConstructor && x instanceof FileConstructor) ||
+      (BlobConstructor !== NoopConstructor && x instanceof BlobConstructor))
   ) {
     return 'null';
   }
@@ -33,16 +34,16 @@ const stringify = (x: any): string => {
   ) {
     const key = cache.get(x) || Math.random().toString(36).slice(2);
     cache.set(x, key);
-    return stringify({ __key: key });
+    return stringify({ __key: key }, includeFiles);
   }
 
   seen.add(x);
   let out = '{';
   for (const key of keys) {
-    const value = stringify(x[key]);
+    const value = stringify(x[key], includeFiles);
     if (value) {
       if (out.length > 1) out += ',';
-      out += stringify(key) + ':' + value;
+      out += stringify(key, includeFiles) + ':' + value;
     }
   }
 
@@ -79,9 +80,9 @@ const extract = (map: FileMap, path: string, x: any): void => {
  * replacing their values, which remain stable for the objects’
  * instance.
  */
-export const stringifyVariables = (x: any): string => {
+export const stringifyVariables = (x: any, includeFiles?: boolean): string => {
   seen.clear();
-  return stringify(x);
+  return stringify(x, includeFiles || false);
 };
 
 class NoopConstructor {}
diff --git a/packages/vue-urql/src/useMutation.ts b/packages/vue-urql/src/useMutation.ts
index d4455ba3..2edd6362 100644
--- a/packages/vue-urql/src/useMutation.ts
+++ b/packages/vue-urql/src/useMutation.ts
@@ -63,7 +63,7 @@ export interface UseMutationResponse<T, V extends AnyVariables = AnyVariables> {
    *
    * @param variables - variables using which the mutation will be executed.
    * @param context - optionally, context options that will be merged with
-   * {@link UseMutationArgs.context} and the `Client`’s options.
+   * the `Client`’s options.
    * @returns the {@link OperationResult} of the mutation.
    *
    * @remarks
diff --git a/packages/vue-urql/src/useQuery.ts b/packages/vue-urql/src/useQuery.ts
index 59a333c0..f298e4bc 100644
--- a/packages/vue-urql/src/useQuery.ts
+++ b/packages/vue-urql/src/useQuery.ts
@@ -1,8 +1,7 @@
 /* eslint-disable react-hooks/rules-of-hooks */
 
-import type { WatchStopHandle, Ref } from 'vue';
-import { isRef } from 'vue';
-import { shallowRef, ref, watchEffect, reactive } from 'vue';
+import type { Ref, WatchStopHandle } from 'vue';
+import { isRef, reactive, ref, shallowRef, watch, watchEffect } from 'vue';
 
 import type { Subscription, Source } from 'wonka';
 import { pipe, subscribe, onEnd } from 'wonka';
@@ -20,6 +19,7 @@ import type {
 import { createRequest } from '@urql/core';
 
 import { useClient } from './useClient';
+
 import type { MaybeRef, MaybeRefObj } from './utils';
 import { unref, updateShallowRef } from './utils';
 
@@ -75,7 +75,7 @@ export type UseQueryArgs<
    * documentation on the `pause` option.
    */
   pause?: MaybeRef<boolean>;
-} & MaybeRefObj<GraphQLRequestParams<Data, Variables>>;
+} & MaybeRefObj<GraphQLRequestParams<Data, MaybeRefObj<Variables>>>;
 
 /** State of the current query, your {@link useQuery} function is executing.
  *
@@ -254,9 +254,10 @@ export function callUseQuery<T = any, V extends AnyVariables = AnyVariables>(
   const operation: Ref<Operation<T, V> | undefined> = ref();
   const extensions: Ref<Record<string, any> | undefined> = ref();
 
-  const isPaused: Ref<boolean> = isRef(_args.pause)
-    ? _args.pause
-    : ref(!!_args.pause);
+  const isPaused: Ref<boolean> = ref(!!unref(args.pause));
+  if (isRef(args.pause) || typeof args.pause === 'function') {
+    stops.push(watch(args.pause, value => (isPaused.value = value)));
+  }
 
   const input = shallowRef({
     request: createRequest<T, V>(unref(args.query), unref(args.variables) as V),
diff --git a/packages/vue-urql/src/useSubscription.ts b/packages/vue-urql/src/useSubscription.ts
index 5c24755b..703700eb 100644
--- a/packages/vue-urql/src/useSubscription.ts
+++ b/packages/vue-urql/src/useSubscription.ts
@@ -3,9 +3,8 @@
 import type { Source } from 'wonka';
 import { pipe, subscribe, onEnd } from 'wonka';
 
-import type { WatchStopHandle, Ref } from 'vue';
-import { isRef } from 'vue';
-import { ref, shallowRef, watchEffect, reactive } from 'vue';
+import type { Ref, WatchStopHandle } from 'vue';
+import { isRef, reactive, ref, shallowRef, watch, watchEffect } from 'vue';
 
 import type {
   Client,
@@ -19,6 +18,7 @@ import type {
 import { createRequest } from '@urql/core';
 
 import { useClient } from './useClient';
+
 import type { MaybeRef, MaybeRefObj } from './utils';
 import { unref, updateShallowRef } from './utils';
 
@@ -36,7 +36,7 @@ export type UseSubscriptionArgs<
    * @remarks
    * `pause` may be set to `true` to stop {@link useSubscription} from starting
    * its subscription automatically. This will pause the subscription until
-   * {@link UseSubscriptonState.resume} is called, or, if `pause` is a reactive
+   * {@link UseSubscriptionResponse.resume} is called, or, if `pause` is a reactive
    * ref of a boolean, until this ref changes to `true`.
    */
   pause?: MaybeRef<boolean>;
@@ -58,7 +58,7 @@ export type UseSubscriptionArgs<
    * ```
    */
   context?: MaybeRef<Partial<OperationContext>>;
-} & MaybeRefObj<GraphQLRequestParams<Data, Variables>>;
+} & MaybeRefObj<GraphQLRequestParams<Data, MaybeRefObj<Variables>>>;
 
 /** Combines previous data with an incoming subscription result’s data.
  *
@@ -143,7 +143,7 @@ export interface UseSubscriptionResponse<
    *
    * @remarks
    * This is the subscription {@link Operation} that is currently active.
-   * When {@link UseQueryState.fetching} is `true`, this is the
+   * When {@link UseSubscriptionResponse.fetching} is `true`, this is the
    * last `Operation` that the current state was for.
    */
   operation: Ref<Operation<T, V> | undefined>;
@@ -254,9 +254,10 @@ export function callUseSubscription<
   const extensions: Ref<Record<string, any> | undefined> = ref();
 
   const scanHandler = ref(handler);
-  const isPaused: Ref<boolean> = isRef(_args.pause)
-    ? _args.pause
-    : ref(!!_args.pause);
+  const isPaused: Ref<boolean> = ref(!!unref(args.pause));
+  if (isRef(args.pause) || typeof args.pause === 'function') {
+    stops.push(watch(args.pause, value => (isPaused.value = value)));
+  }
 
   const input = shallowRef({
     request: createRequest<T, V>(unref(args.query), unref(args.variables) as V),
diff --git a/packages/vue-urql/src/utils.ts b/packages/vue-urql/src/utils.ts
index 42d82279..a9a83cda 100644
--- a/packages/vue-urql/src/utils.ts
+++ b/packages/vue-urql/src/utils.ts
@@ -3,7 +3,9 @@ import type { Ref, ShallowRef } from 'vue';
 import { isRef } from 'vue';
 
 export type MaybeRef<T> = T | (() => T) | Ref<T>;
-export type MaybeRefObj<T extends {}> = { [K in keyof T]: MaybeRef<T[K]> };
+export type MaybeRefObj<T> = T extends {}
+  ? { [K in keyof T]: MaybeRef<T[K]> }
+  : T;
 
 export const unref = <T>(maybeRef: MaybeRef<T>): T =>
   typeof maybeRef === 'function'


Test patch:
diff --git a/exchanges/graphcache/src/cacheExchange.test.ts b/exchanges/graphcache/src/cacheExchange.test.ts
index fcb5b2bb..fb7b6c41 100644
--- a/exchanges/graphcache/src/cacheExchange.test.ts
+++ b/exchanges/graphcache/src/cacheExchange.test.ts
@@ -3852,3 +3852,122 @@ describe('commutativity', () => {
     expect(failingData).not.toMatchObject({ hasNext: true });
   });
 });
+
+describe('abstract types', () => {
+  it('works with two responses giving different concrete types for a union', () => {
+    const query = gql`
+      query ($id: ID!) {
+        field(id: $id) {
+          id
+          union {
+            ... on Type1 {
+              id
+              name
+              __typename
+            }
+            ... on Type2 {
+              id
+              title
+              __typename
+            }
+          }
+          __typename
+        }
+      }
+    `;
+    const client = createClient({
+      url: 'http://0.0.0.0',
+      exchanges: [],
+    });
+    const { source: ops$, next } = makeSubject<Operation>();
+    const operation1 = client.createRequestOperation('query', {
+      key: 1,
+      query,
+      variables: { id: '1' },
+    });
+    const operation2 = client.createRequestOperation('query', {
+      key: 2,
+      query,
+      variables: { id: '2' },
+    });
+    const queryResult1: OperationResult = {
+      ...queryResponse,
+      operation: operation1,
+      data: {
+        __typename: 'Query',
+        field: {
+          id: '1',
+          __typename: 'Todo',
+          union: {
+            id: '1',
+            name: 'test',
+            __typename: 'Type1',
+          },
+        },
+      },
+    };
+
+    const queryResult2: OperationResult = {
+      ...queryResponse,
+      operation: operation2,
+      data: {
+        __typename: 'Query',
+        field: {
+          id: '2',
+          __typename: 'Todo',
+          union: {
+            id: '2',
+            title: 'test',
+            __typename: 'Type2',
+          },
+        },
+      },
+    };
+
+    vi.spyOn(client, 'reexecuteOperation').mockImplementation(next);
+    const response = vi.fn((forwardOp: Operation): OperationResult => {
+      if (forwardOp.key === 1) return queryResult1;
+      if (forwardOp.key === 2) return queryResult2;
+      return undefined as any;
+    });
+
+    const result = vi.fn();
+    const forward: ExchangeIO = ops$ => pipe(ops$, map(response), share);
+
+    pipe(
+      cacheExchange({})({ forward, client, dispatchDebug })(ops$),
+      tap(result),
+      publish
+    );
+
+    next(operation1);
+    expect(response).toHaveBeenCalledTimes(1);
+    expect(result).toHaveBeenCalledTimes(1);
+    expect(result.mock.calls[0][0].data).toEqual({
+      field: {
+        __typename: 'Todo',
+        id: '1',
+        union: {
+          __typename: 'Type1',
+          id: '1',
+          name: 'test',
+        },
+      },
+    });
+
+    next(operation2);
+    expect(response).toHaveBeenCalledTimes(2);
+    expect(result).toHaveBeenCalledTimes(2);
+    expect(result.mock.calls[1][0].data).toEqual({
+      field: {
+        __typename: 'Todo',
+        id: '2',
+        union: {
+          __typename: 'Type2',
+          id: '2',
+          title: 'test',
+        },
+      },
+    });
+  });
+});
diff --git a/packages/vue-urql/src/useQuery.test.ts b/packages/vue-urql/src/useQuery.test.ts
index 6ac721cc..5ba38be7 100644
--- a/packages/vue-urql/src/useQuery.test.ts
+++ b/packages/vue-urql/src/useQuery.test.ts
@@ -116,15 +116,17 @@ describe('useQuery', () => {
         () => subject.source as OperationResultSource<OperationResult>
       );
 
+    const pause = ref(true);
+
     const _query = useQuery({
       query: `{ test }`,
-      pause: true,
+      pause: () => pause.value,
     });
     const query = reactive(_query);
 
     expect(executeQuery).not.toHaveBeenCalled();
 
-    query.resume();
+    pause.value = false;
     await nextTick();
     expect(query.fetching).toBe(true);