Repository: urql-graphql/urql
Pull request #3608 (2024-06-10 05:40:31)

docs: Add `urql-exhaustive-additional-typenames-exchange` to showcase
<!--
  Thanks for opening a pull request! We appreciate your dedication and help!
  Before submitting your pull request, please make sure to read our CONTRIBUTING guide.

  The best contribution is always a PR, but please make sure to open an issue or discuss
  your changes first, if you’re looking to submit a larger PR.

  If this PR is already related to an issue, please reference it like so:
  Resolves #123
-->

## Summary

I have been using the exchange referred to here https://github.com/urql-graphql/urql/discussions/3440#discussioncomment-8768504 for several months and it's useful, I would like to add it to the documentation.



changeset-bot[bot]: ###  ⚠️  No Changeset found

Latest commit: 1dfac82747907cf6c388857750cea4688b991607

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. **If these changes should result in a version bump, you need to add a changeset.**

<details><summary>This PR includes no changesets</summary>

  When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

</details>

[Click here to learn what changesets are, and how to add one](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add a changeset to this PR](https://github.com/MH4GF/urql/new/patch-1?filename=.changeset/breezy-melons-study.md&value=---%0A%0A---%0A%0AUpdate%20showcase.md%20to%20add%20urql-exhaustive-additional-typenames-exchange%0A)



Patch:
diff --git a/.changeset/fluffy-seals-confess.md b/.changeset/fluffy-seals-confess.md
deleted file mode 100644
index 7215672d..00000000
--- a/.changeset/fluffy-seals-confess.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@urql/vue': patch
----
-
-Fix reactaive typings for `variables` (See [#3605](https://github.com/urql-graphql/urql/pull/3605))
diff --git a/.changeset/old-humans-knock.md b/.changeset/old-humans-knock.md
new file mode 100644
index 00000000..e87d696a
--- /dev/null
+++ b/.changeset/old-humans-knock.md
@@ -0,0 +1,5 @@
+---
+'@urql/vue': patch
+---
+
+Use `shallowRef` for data variable to avoid extra overhead for heavy objects
diff --git a/docs/architecture.md b/docs/architecture.md
index b90d6941..a237db59 100644
--- a/docs/architecture.md
+++ b/docs/architecture.md
@@ -10,7 +10,7 @@ As you use it in your app, it's split into three parts:
 
 - Bindings — such as for React, Preact, Vue, or Svelte — which interact with `@urql/core`'s
   `Client`.
-- The Client — as created [with the core `@urql/core` package](./basics/code.md), which interacts with "exchanges" to execute GraphQL
+- The Client — as created [with the core `@urql/core` package](./basics/core.md), which interacts with "exchanges" to execute GraphQL
   operations, and which you can also use directly.
 - Exchanges, which provide functionality like fetching or caching to the `Client`.
 
diff --git a/docs/showcase.md b/docs/showcase.md
index 651c4d9d..fb1df049 100644
--- a/docs/showcase.md
+++ b/docs/showcase.md
@@ -67,3 +67,4 @@ maintainers and supporters. This page is specifically dedicated to all of you!
 - [`@grafbase/urql-exchange`](https://github.com/grafbase/playground/tree/main/packages/grafbase-urql-exchange): URQL-exchange for handling Server Sent Events (SSE) with Grafbase GraphQL Live Queries.
   to automatically convert scalars.
 - [`urql-rest-exchange`](https://github.com/iamsavani/urql-rest-exchange): A custom exchange for URQL that supports GraphQL queries/mutations via REST endpoints
+- [`urql-exhaustive-additional-typenames-exchange`](https://github.com/route06/urql-exhaustive-additional-typenames-exchange): URQL-exchange that add all list fields of the operation to additionalTypenames to help document caching
diff --git a/examples/with-apq/package.json b/examples/with-apq/package.json
index b5869959..05be11fa 100644
--- a/examples/with-apq/package.json
+++ b/examples/with-apq/package.json
@@ -6,7 +6,7 @@
     "start": "vite"
   },
   "dependencies": {
-    "@urql/core": "^5.0.4",
+    "@urql/core": "^5.0.5",
     "@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 24361663..559ebd70 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.4",
-    "@urql/exchange-graphcache": "^7.1.1",
+    "@urql/core": "^5.0.5",
+    "@urql/exchange-graphcache": "^7.1.2",
     "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 7298a1ad..6d00eee5 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.4",
-    "@urql/exchange-graphcache": "^7.1.1",
+    "@urql/core": "^5.0.5",
+    "@urql/exchange-graphcache": "^7.1.2",
     "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 81089d76..f3e84ebc 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.4",
+    "@urql/core": "^5.0.5",
     "@urql/exchange-auth": "^2.2.0",
-    "@urql/exchange-graphcache": "^7.1.1",
+    "@urql/exchange-graphcache": "^7.1.2",
     "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 daac894a..74b11273 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.4",
+    "@urql/core": "^5.0.5",
     "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 e7b9f3e5..889bd9ad 100644
--- a/examples/with-multipart/package.json
+++ b/examples/with-multipart/package.json
@@ -6,7 +6,7 @@
     "start": "vite"
   },
   "dependencies": {
-    "@urql/core": "^5.0.4",
+    "@urql/core": "^5.0.5",
     "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 a78661df..e55e8bc0 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.4",
+    "@urql/core": "^5.0.5",
     "@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 daac894a..74b11273 100644
--- a/examples/with-pagination/package.json
+++ b/examples/with-pagination/package.json
@@ -6,7 +6,7 @@
     "start": "vite"
   },
   "dependencies": {
-    "@urql/core": "^5.0.4",
+    "@urql/core": "^5.0.5",
     "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 b3a1ab52..e13d89eb 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.4",
+    "@urql/core": "^5.0.5",
     "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 8073e3c9..506f24ac 100644
--- a/examples/with-react/package.json
+++ b/examples/with-react/package.json
@@ -6,7 +6,7 @@
     "start": "vite"
   },
   "dependencies": {
-    "@urql/core": "^5.0.4",
+    "@urql/core": "^5.0.5",
     "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 38a1c5cb..00d350cc 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.4",
+    "@urql/core": "^5.0.5",
     "@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 2642010c..9d337b75 100644
--- a/examples/with-retry/package.json
+++ b/examples/with-retry/package.json
@@ -6,7 +6,7 @@
     "start": "vite"
   },
   "dependencies": {
-    "@urql/core": "^5.0.4",
+    "@urql/core": "^5.0.5",
     "@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 e53dede0..7f87480d 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.4",
-    "@urql/exchange-graphcache": "^7.1.1",
+    "@urql/core": "^5.0.5",
+    "@urql/exchange-graphcache": "^7.1.2",
     "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 5c48c9b8..0a2cf425 100644
--- a/examples/with-svelte/package.json
+++ b/examples/with-svelte/package.json
@@ -8,8 +8,8 @@
     "serve": "vite preview"
   },
   "dependencies": {
-    "@urql/core": "^5.0.4",
-    "@urql/svelte": "^4.2.0",
+    "@urql/core": "^5.0.5",
+    "@urql/svelte": "^4.2.1",
     "graphql": "^16.6.0",
     "svelte": "^4.0.5"
   },
diff --git a/examples/with-vue3/package.json b/examples/with-vue3/package.json
index b4f36bea..9b9c554e 100644
--- a/examples/with-vue3/package.json
+++ b/examples/with-vue3/package.json
@@ -8,8 +8,8 @@
     "serve": "vite preview"
   },
   "dependencies": {
-    "@urql/core": "^5.0.4",
-    "@urql/vue": "^1.2.1",
+    "@urql/core": "^5.0.5",
+    "@urql/vue": "^1.4.0",
     "graphql": "^16.6.0",
     "vue": "^3.2.47"
   },
diff --git a/exchanges/graphcache/CHANGELOG.md b/exchanges/graphcache/CHANGELOG.md
index b149eada..86b2983b 100644
--- a/exchanges/graphcache/CHANGELOG.md
+++ b/exchanges/graphcache/CHANGELOG.md
@@ -1,5 +1,12 @@
 # @urql/exchange-graphcache
 
+## 7.1.2
+
+### Patch Changes
+
+- Disregard write-only operation when fragment-matching with schema awareness
+  Submitted by [@JoviDeCroock](https://github.com/JoviDeCroock) (See [#3621](https://github.com/urql-graphql/urql/pull/3621))
+
 ## 7.1.1
 
 ### Patch Changes
diff --git a/exchanges/graphcache/jsr.json b/exchanges/graphcache/jsr.json
index 4949dfa6..16120bba 100644
--- a/exchanges/graphcache/jsr.json
+++ b/exchanges/graphcache/jsr.json
@@ -1,6 +1,6 @@
 {
   "name": "@urql/exchange-graphcache",
-  "version": "7.1.1",
+  "version": "7.1.2",
   "exports": {
     ".": "./src/index.ts",
     "./extras": "./src/extras/index.ts",
diff --git a/exchanges/graphcache/package.json b/exchanges/graphcache/package.json
index 2282cfe9..5fc70afd 100644
--- a/exchanges/graphcache/package.json
+++ b/exchanges/graphcache/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@urql/exchange-graphcache",
-  "version": "7.1.1",
+  "version": "7.1.2",
   "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 4dad5e09..bb1893a3 100644
--- a/exchanges/graphcache/src/operations/shared.ts
+++ b/exchanges/graphcache/src/operations/shared.ts
@@ -236,7 +236,10 @@ export function makeSelectionIterator(
                     ctx.store.logger
                   ));
 
-            if (isMatching || currentOperation === 'write') {
+            if (
+              isMatching ||
+              (currentOperation === 'write' && !ctx.store.schema)
+            ) {
               if (process.env.NODE_ENV !== 'production')
                 pushDebugNode(typename, fragment);
               const isFragmentOptional = isOptional(select);
diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md
index 48cab2f3..21554fa2 100644
--- a/packages/core/CHANGELOG.md
+++ b/packages/core/CHANGELOG.md
@@ -1,5 +1,12 @@
 # @urql/core
 
+## 5.0.5
+
+### Patch Changes
+
+- Removes double serialization of `data` in `ssrExchange`
+  Submitted by [@negezor](https://github.com/negezor) (See [#3632](https://github.com/urql-graphql/urql/pull/3632))
+
 ## 5.0.4
 
 ### Patch Changes
diff --git a/packages/core/jsr.json b/packages/core/jsr.json
index 0d99437a..fb4c7303 100644
--- a/packages/core/jsr.json
+++ b/packages/core/jsr.json
@@ -1,6 +1,6 @@
 {
   "name": "@urql/core",
-  "version": "5.0.4",
+  "version": "5.0.5",
   "exports": {
     ".": "./src/index.ts",
     "./internal": "./src/internal/index.ts"
diff --git a/packages/core/package.json b/packages/core/package.json
index 6a16c5ab..bfb33195 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@urql/core",
-  "version": "5.0.4",
+  "version": "5.0.5",
   "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/exchanges/ssr.ts b/packages/core/src/exchanges/ssr.ts
index 10fff64c..6716a54f 100644
--- a/packages/core/src/exchanges/ssr.ts
+++ b/packages/core/src/exchanges/ssr.ts
@@ -109,7 +109,6 @@ const serializeResult = (
   includeExtensions: boolean
 ): SerializedResult => {
   const serialized: SerializedResult = {
-    data: JSON.stringify(result.data),
     hasNext: result.hasNext,
   };
 
diff --git a/packages/svelte-urql/CHANGELOG.md b/packages/svelte-urql/CHANGELOG.md
index 9946f28b..fef987e7 100644
--- a/packages/svelte-urql/CHANGELOG.md
+++ b/packages/svelte-urql/CHANGELOG.md
@@ -1,5 +1,12 @@
 # @urql/svelte
 
+## 4.2.1
+
+### Patch Changes
+
+- add support for Svelte 5 in the `peerDependencies`
+  Submitted by [@itssumitrai](https://github.com/itssumitrai) (See [#3634](https://github.com/urql-graphql/urql/pull/3634))
+
 ## 4.2.0
 
 ### Minor Changes
diff --git a/packages/svelte-urql/jsr.json b/packages/svelte-urql/jsr.json
index ac8bb4a7..25fe911c 100644
--- a/packages/svelte-urql/jsr.json
+++ b/packages/svelte-urql/jsr.json
@@ -1,6 +1,6 @@
 {
   "name": "@urql/svelte",
-  "version": "4.2.0",
+  "version": "4.2.1",
   "exports": {
     ".": "./src/index.ts"
   },
diff --git a/packages/svelte-urql/package.json b/packages/svelte-urql/package.json
index 0a934ddd..bdb5b4e8 100644
--- a/packages/svelte-urql/package.json
+++ b/packages/svelte-urql/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@urql/svelte",
-  "version": "4.2.0",
+  "version": "4.2.1",
   "description": "A highly customizable and versatile GraphQL client for Svelte",
   "sideEffects": false,
   "homepage": "https://formidable.com/open-source/urql/docs/",
@@ -50,7 +50,7 @@
   },
   "peerDependencies": {
     "@urql/core": "^5.0.0",
-    "svelte": "^3.0.0 || ^4.0.0"
+    "svelte": "^3.0.0 || ^4.0.0 || ^5.0.0"
   },
   "dependencies": {
     "@urql/core": "^5.0.0",
diff --git a/packages/vue-urql/CHANGELOG.md b/packages/vue-urql/CHANGELOG.md
index 11350e00..1904c537 100644
--- a/packages/vue-urql/CHANGELOG.md
+++ b/packages/vue-urql/CHANGELOG.md
@@ -1,5 +1,44 @@
 # @urql/vue
 
+## 1.4.0
+
+### Minor Changes
+
+- Refactor composable functions with a focus on avoiding memory leaks and Vue best practices
+  Submitted by [@yurks](https://github.com/yurks) (See [#3619](https://github.com/urql-graphql/urql/pull/3619))
+
+## 1.3.2
+
+### Patch Changes
+
+- ⚠️ Fix deep options reactivity for subscriptions
+  Submitted by [@JoviDeCroock](https://github.com/JoviDeCroock) (See [#3616](https://github.com/urql-graphql/urql/pull/3616))
+
+## 1.3.1
+
+### Patch Changes
+
+- ⚠️ Fix variables losing reactivity
+  Submitted by [@JoviDeCroock](https://github.com/JoviDeCroock) (See [#3614](https://github.com/urql-graphql/urql/pull/3614))
+
+## 1.3.0
+
+### Minor Changes
+
+- Use `shallowRef` to avoid creating deeply reactive objects for heavy objects
+  Submitted by [@negezor](https://github.com/negezor) (See [#3611](https://github.com/urql-graphql/urql/pull/3611))
+- Remove wrapping request `args` in `reactive` to fix memory leak
+  Submitted by [@negezor](https://github.com/negezor) (See [#3612](https://github.com/urql-graphql/urql/pull/3612))
+
+## 1.2.2
+
+### Patch Changes
+
+- ⚠️ Fix reactaive typings for `variables` (See [#3605](https://github.com/urql-graphql/urql/pull/3605))
+  Submitted by [@JoviDeCroock](https://github.com/JoviDeCroock) (See [`118d74b2`](https://github.com/urql-graphql/urql/commit/118d74b238007264cacfb91fc12de74370d5766e))
+- Restore the possibility to use a getter for the pause property
+  Submitted by [@arkandias](https://github.com/arkandias) (See [#3598](https://github.com/urql-graphql/urql/pull/3598))
+
 ## 1.2.1
 
 ### Patch Changes
diff --git a/packages/vue-urql/jsr.json b/packages/vue-urql/jsr.json
index e5249e54..7a77fe5e 100644
--- a/packages/vue-urql/jsr.json
+++ b/packages/vue-urql/jsr.json
@@ -1,6 +1,6 @@
 {
   "name": "@urql/vue",
-  "version": "1.2.1",
+  "version": "1.4.0",
   "exports": {
     ".": "./src/index.ts"
   },
diff --git a/packages/vue-urql/package.json b/packages/vue-urql/package.json
index 2ae56cda..4ddf7856 100644
--- a/packages/vue-urql/package.json
+++ b/packages/vue-urql/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@urql/vue",
-  "version": "1.2.1",
+  "version": "1.4.0",
   "description": "A highly customizable and versatile GraphQL client for vue",
   "sideEffects": false,
   "homepage": "https://formidable.com/open-source/urql/docs/",
diff --git a/packages/vue-urql/src/useClient.ts b/packages/vue-urql/src/useClient.ts
index e1846e7f..ae9bd14a 100644
--- a/packages/vue-urql/src/useClient.ts
+++ b/packages/vue-urql/src/useClient.ts
@@ -1,5 +1,5 @@
 import type { App, Ref } from 'vue';
-import { getCurrentInstance, inject, provide, isRef, ref } from 'vue';
+import { getCurrentInstance, inject, provide, isRef, shallowRef } from 'vue';
 import type { ClientOptions } from '@urql/core';
 import { Client } from '@urql/core';
 
@@ -36,7 +36,7 @@ const clientsPerInstance = new WeakMap<{}, Ref<Client>>();
 export function provideClient(opts: ClientOptions | Client | Ref<Client>) {
   let client: Ref<Client>;
   if (!isRef(opts)) {
-    client = ref(opts instanceof Client ? opts : new Client(opts));
+    client = shallowRef(opts instanceof Client ? opts : new Client(opts));
   } else {
     client = opts;
   }
@@ -78,7 +78,7 @@ export function provideClient(opts: ClientOptions | Client | Ref<Client>) {
 export function install(app: App, opts: ClientOptions | Client | Ref<Client>) {
   let client: Ref<Client>;
   if (!isRef(opts)) {
-    client = ref(opts instanceof Client ? opts : new Client(opts));
+    client = shallowRef(opts instanceof Client ? opts : new Client(opts));
   } else {
     client = opts;
   }
diff --git a/packages/vue-urql/src/useClientHandle.ts b/packages/vue-urql/src/useClientHandle.ts
index 962f2bed..60988d82 100644
--- a/packages/vue-urql/src/useClientHandle.ts
+++ b/packages/vue-urql/src/useClientHandle.ts
@@ -1,5 +1,4 @@
-import type { DocumentNode } from 'graphql';
-import type { AnyVariables, Client, TypedDocumentNode } from '@urql/core';
+import type { AnyVariables, Client, DocumentInput } from '@urql/core';
 import type { WatchStopHandle } from 'vue';
 import { getCurrentInstance, onMounted, onBeforeUnmount } from 'vue';
 
@@ -75,7 +74,7 @@ export interface ClientHandle {
    * function or when chained in an `async setup()` function.
    */
   useMutation<T = any, V extends AnyVariables = AnyVariables>(
-    query: TypedDocumentNode<T, V> | DocumentNode | string
+    query: DocumentInput<T, V>
   ): UseMutationResponse<T, V>;
 }
 
@@ -153,7 +152,7 @@ export function useClientHandle(): ClientHandle {
     },
 
     useMutation<T = any, V extends AnyVariables = AnyVariables>(
-      query: TypedDocumentNode<T, V> | DocumentNode | string
+      query: DocumentInput<T, V>
     ): UseMutationResponse<T, V> {
       return callUseMutation(query, client);
     },
diff --git a/packages/vue-urql/src/useMutation.ts b/packages/vue-urql/src/useMutation.ts
index d4455ba3..e5856783 100644
--- a/packages/vue-urql/src/useMutation.ts
+++ b/packages/vue-urql/src/useMutation.ts
@@ -1,24 +1,22 @@
 /* eslint-disable react-hooks/rules-of-hooks */
 
 import type { Ref } from 'vue';
-import { ref } from 'vue';
-import type { DocumentNode } from 'graphql';
+import { shallowRef } from 'vue';
 import { pipe, onPush, filter, toPromise, take } from 'wonka';
 
 import type {
   Client,
   AnyVariables,
-  TypedDocumentNode,
   CombinedError,
   Operation,
   OperationContext,
   OperationResult,
+  DocumentInput,
 } from '@urql/core';
-import { createRequest } from '@urql/core';
 
 import { useClient } from './useClient';
 import type { MaybeRef } from './utils';
-import { unref } from './utils';
+import { createRequestWithArgs, useRequestState } from './utils';
 
 /** State of the last mutation executed by {@link useMutation}.
  *
@@ -63,7 +61,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
@@ -126,21 +124,21 @@ export interface UseMutationResponse<T, V extends AnyVariables = AnyVariables> {
  * ```
  */
 export function useMutation<T = any, V extends AnyVariables = AnyVariables>(
-  query: TypedDocumentNode<T, V> | DocumentNode | string
+  query: DocumentInput<T, V>
 ): UseMutationResponse<T, V> {
   return callUseMutation(query);
 }
 
 export function callUseMutation<T = any, V extends AnyVariables = AnyVariables>(
-  query: MaybeRef<TypedDocumentNode<T, V> | DocumentNode | string>,
+  query: MaybeRef<DocumentInput<T, V>>,
   client: Ref<Client> = useClient()
 ): UseMutationResponse<T, V> {
-  const data: Ref<T | undefined> = ref();
-  const stale: Ref<boolean> = ref(false);
-  const fetching: Ref<boolean> = ref(false);
-  const error: Ref<CombinedError | undefined> = ref();
-  const operation: Ref<Operation<T, V> | undefined> = ref();
-  const extensions: Ref<Record<string, any> | undefined> = ref();
+  const data: Ref<T | undefined> = shallowRef();
+
+  const { fetching, operation, extensions, stale, error } = useRequestState<
+    T,
+    V
+  >();
 
   return {
     data,
@@ -157,7 +155,7 @@ export function callUseMutation<T = any, V extends AnyVariables = AnyVariables>(
 
       return pipe(
         client.value.executeMutation<T, V>(
-          createRequest<T, V>(unref(query), unref(variables)),
+          createRequestWithArgs({ query, variables }),
           context || {}
         ),
         onPush(result => {
diff --git a/packages/vue-urql/src/useQuery.ts b/packages/vue-urql/src/useQuery.ts
index 5d68fe17..d8da69b7 100644
--- a/packages/vue-urql/src/useQuery.ts
+++ b/packages/vue-urql/src/useQuery.ts
@@ -1,27 +1,25 @@
 /* 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 { shallowRef, watchEffect } from 'vue';
 
-import type { Subscription, Source } from 'wonka';
+import type { Subscription } from 'wonka';
 import { pipe, subscribe, onEnd } from 'wonka';
 
 import type {
   Client,
   AnyVariables,
-  OperationResult,
   GraphQLRequestParams,
   CombinedError,
   OperationContext,
   RequestPolicy,
   Operation,
 } from '@urql/core';
-import { createRequest } from '@urql/core';
 
 import { useClient } from './useClient';
+
 import type { MaybeRef, MaybeRefObj } from './utils';
-import { unref, updateShallowRef } from './utils';
+import { useRequestState, useClientState } from './utils';
 
 /** Input arguments for the {@link useQuery} function.
  *
@@ -195,10 +193,6 @@ export type UseQueryResponse<
   V extends AnyVariables = AnyVariables,
 > = UseQueryState<T, V> & PromiseLike<UseQueryState<T, V>>;
 
-const watchOptions = {
-  flush: 'pre' as const,
-};
-
 /** Function to run a GraphQL query and get reactive GraphQL results.
  *
  * @param args - a {@link UseQueryArgs} object, to pass a `query`, `variables`, and options.
@@ -241,54 +235,83 @@ export function useQuery<T = any, V extends AnyVariables = AnyVariables>(
 }
 
 export function callUseQuery<T = any, V extends AnyVariables = AnyVariables>(
-  _args: UseQueryArgs<T, V>,
+  args: UseQueryArgs<T, V>,
   client: Ref<Client> = useClient(),
-  stops: WatchStopHandle[] = []
+  stops?: WatchStopHandle[]
 ): UseQueryResponse<T, V> {
-  const args = reactive(_args) as UseQueryArgs<T, V>;
+  const data: Ref<T | undefined> = shallowRef();
 
-  const data: Ref<T | undefined> = ref();
-  const stale: Ref<boolean> = ref(false);
-  const fetching: Ref<boolean> = ref(false);
-  const error: Ref<CombinedError | undefined> = ref();
-  const operation: Ref<Operation<T, V> | undefined> = ref();
-  const extensions: Ref<Record<string, any> | undefined> = ref();
+  const { fetching, operation, extensions, stale, error } = useRequestState<
+    T,
+    V
+  >();
 
-  const isPaused: Ref<boolean> = isRef(_args.pause)
-    ? _args.pause
-    : ref(!!_args.pause);
-
-  const input = shallowRef({
-    request: createRequest<T, V>(unref(args.query), unref(args.variables) as V),
-    requestPolicy: unref(args.requestPolicy),
-    isPaused: isPaused.value,
-  });
+  const { isPaused, source, pause, resume, execute, teardown } = useClientState(
+    args,
+    client,
+    'executeQuery'
+  );
 
-  const source: Ref<Source<OperationResult<T, V>> | undefined> = ref();
+  const teardownQuery = watchEffect(
+    onInvalidate => {
+      if (source.value) {
+        fetching.value = true;
+        stale.value = false;
 
-  stops.push(
-    watchEffect(() => {
-      updateShallowRef(input, {
-        request: createRequest<T, V>(
-          unref(args.query),
-          unref(args.variables) as V
-        ),
-        requestPolicy: unref(args.requestPolicy),
-        isPaused: isPaused.value,
-      });
-    }, watchOptions)
+        onInvalidate(
+          pipe(
+            source.value,
+            onEnd(() => {
+              fetching.value = false;
+              stale.value = false;
+            }),
+            subscribe(res => {
+              data.value = res.data;
+              stale.value = !!res.stale;
+              fetching.value = false;
+              error.value = res.error;
+              operation.value = res.operation;
+              extensions.value = res.extensions;
+            })
+          ).unsubscribe
+        );
+      } else {
+        fetching.value = false;
+        stale.value = false;
+      }
+    },
+    {
+      // NOTE: This part of the query pipeline is only initialised once and will need
+      // to do so synchronously
+      flush: 'sync',
+    }
   );
 
-  stops.push(
-    watchEffect(() => {
-      source.value = !input.value.isPaused
-        ? client.value.executeQuery<T, V>(input.value.request, {
-            requestPolicy: unref(args.requestPolicy),
-            ...unref(args.context),
-          })
-        : undefined;
-    }, watchOptions)
-  );
+  stops && stops.push(teardown, teardownQuery);
+
+  const then: UseQueryResponse<T, V>['then'] = (onFulfilled, onRejected) => {
+    let sub: Subscription | void;
+
+    const promise = new Promise<UseQueryState<T, V>>(resolve => {
+      if (!source.value) {
+        return resolve(state);
+      }
+      let hasResult = false;
+      sub = pipe(
+        source.value,
+        subscribe(() => {
+          if (!state.fetching.value && !state.stale.value) {
+            if (sub) sub.unsubscribe();
+            hasResult = true;
+            resolve(state);
+          }
+        })
+      );
+      if (hasResult) sub.unsubscribe();
+    });
+
+    return promise.then(onFulfilled, onRejected);
+  };
 
   const state: UseQueryState<T, V> = {
     data,
@@ -298,105 +321,13 @@ export function callUseQuery<T = any, V extends AnyVariables = AnyVariables>(
     extensions,
     fetching,
     isPaused,
+    pause,
+    resume,
     executeQuery(opts?: Partial<OperationContext>): UseQueryResponse<T, V> {
-      const s = (source.value = client.value.executeQuery<T, V>(
-        input.value.request,
-        {
-          requestPolicy: unref(args.requestPolicy),
-          ...unref(args.context),
-          ...opts,
-        }
-      ));
-
-      return {
-        ...response,
-        then(onFulfilled, onRejected) {
-          let sub: Subscription | void;
-          return new Promise<UseQueryState<T, V>>(resolve => {
-            let hasResult = false;
-            sub = pipe(
-              s,
-              subscribe(() => {
-                if (!state.fetching.value && !state.stale.value) {
-                  if (sub) sub.unsubscribe();
-                  hasResult = true;
-                  resolve(state);
-                }
-              })
-            );
-            if (hasResult) sub.unsubscribe();
-          }).then(onFulfilled, onRejected);
-        },
-      };
-    },
-    pause() {
-      isPaused.value = true;
-    },
-    resume() {
-      isPaused.value = false;
-    },
-  };
-
-  stops.push(
-    watchEffect(
-      onInvalidate => {
-        if (source.value) {
-          fetching.value = true;
-          stale.value = false;
-
-          onInvalidate(
-            pipe(
-              source.value,
-              onEnd(() => {
-                fetching.value = false;
-                stale.value = false;
-              }),
-              subscribe(res => {
-                data.value = res.data;
-                stale.value = !!res.stale;
-                fetching.value = false;
-                error.value = res.error;
-                operation.value = res.operation;
-                extensions.value = res.extensions;
-              })
-            ).unsubscribe
-          );
-        } else {
-          fetching.value = false;
-          stale.value = false;
-        }
-      },
-      {
-        // NOTE: This part of the query pipeline is only initialised once and will need
-        // to do so synchronously
-        flush: 'sync',
-      }
-    )
-  );
-
-  const response: UseQueryResponse<T, V> = {
-    ...state,
-    then(onFulfilled, onRejected) {
-      let sub: Subscription | void;
-      const promise = new Promise<UseQueryState<T, V>>(resolve => {
-        if (!source.value) return resolve(state);
-        let hasResult = false;
-        sub = pipe(
-          source.value,
-          subscribe(() => {
-            if (!state.fetching.value && !state.stale.value) {
-              if (sub) sub.unsubscribe();
-              hasResult = true;
-              resolve(state);
-            }
-          })
-        );
-        if (hasResult) sub.unsubscribe();
-      });
-
-      return promise.then(onFulfilled, onRejected);
+      execute(opts);
+      return { ...state, then };
     },
   };
 
-  return response;
+  return { ...state, then };
 }
diff --git a/packages/vue-urql/src/useSubscription.ts b/packages/vue-urql/src/useSubscription.ts
index 83f17174..b80991f7 100644
--- a/packages/vue-urql/src/useSubscription.ts
+++ b/packages/vue-urql/src/useSubscription.ts
@@ -1,26 +1,23 @@
 /* eslint-disable react-hooks/rules-of-hooks */
 
-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 { shallowRef, isRef, watchEffect } from 'vue';
 
 import type {
   Client,
   GraphQLRequestParams,
   AnyVariables,
-  OperationResult,
   CombinedError,
   OperationContext,
   Operation,
 } from '@urql/core';
-import { createRequest } from '@urql/core';
 
 import { useClient } from './useClient';
+
 import type { MaybeRef, MaybeRefObj } from './utils';
-import { unref, updateShallowRef } from './utils';
+import { useRequestState, useClientState } from './utils';
 
 /** Input arguments for the {@link useSubscription} function.
  *
@@ -36,7 +33,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>;
@@ -89,7 +86,9 @@ export type UseSubscriptionArgs<
 export type SubscriptionHandler<T, R> = (prev: R | undefined, data: T) => R;
 
 /** A {@link SubscriptionHandler} or a reactive ref of one. */
-export type SubscriptionHandlerArg<T, R> = MaybeRef<SubscriptionHandler<T, R>>;
+export type SubscriptionHandlerArg<T, R> =
+  | Ref<SubscriptionHandler<T, R>>
+  | SubscriptionHandler<T, R>;
 
 /** State of the current query, your {@link useSubscription} function is executing.
  *
@@ -143,7 +142,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>;
@@ -182,10 +181,6 @@ export interface UseSubscriptionResponse<
   executeSubscription(opts?: Partial<OperationContext>): void;
 }
 
-const watchOptions = {
-  flush: 'pre' as const,
-};
-
 /** Function to run a GraphQL subscription and get reactive GraphQL results.
  *
  * @param args - a {@link UseSubscriptionArgs} object, to pass a `query`, `variables`, and options.
@@ -229,7 +224,7 @@ export function useSubscription<
   V extends AnyVariables = AnyVariables,
 >(
   args: UseSubscriptionArgs<T, V>,
-  handler?: MaybeRef<SubscriptionHandler<T, R>>
+  handler?: SubscriptionHandlerArg<T, R>
 ): UseSubscriptionResponse<T, R, V> {
   return callUseSubscription(args, handler);
 }
@@ -239,85 +234,58 @@ export function callUseSubscription<
   R = T,
   V extends AnyVariables = AnyVariables,
 >(
-  _args: UseSubscriptionArgs<T, V>,
-  handler?: MaybeRef<SubscriptionHandler<T, R>>,
+  args: UseSubscriptionArgs<T, V>,
+  handler?: SubscriptionHandlerArg<T, R>,
   client: Ref<Client> = useClient(),
-  stops: WatchStopHandle[] = []
+  stops?: WatchStopHandle[]
 ): UseSubscriptionResponse<T, R, V> {
-  const args = reactive(_args) as UseSubscriptionArgs<T, V>;
-
-  const data: Ref<R | undefined> = ref();
-  const stale: Ref<boolean> = ref(false);
-  const fetching: Ref<boolean> = ref(false);
-  const error: Ref<CombinedError | undefined> = ref();
-  const operation: Ref<Operation<T, V> | undefined> = ref();
-  const extensions: Ref<Record<string, any> | undefined> = ref();
+  const data: Ref<R | undefined> = shallowRef();
 
-  const scanHandler = ref(handler);
-  const isPaused: Ref<boolean> = isRef(_args.pause)
-    ? _args.pause
-    : ref(!!_args.pause);
+  const { fetching, operation, extensions, stale, error } = useRequestState<
+    T,
+    V
+  >();
 
-  const input = shallowRef({
-    request: createRequest<T, V>(unref(args.query), unref(args.variables) as V),
-    isPaused: isPaused.value,
-  });
+  const { isPaused, source, pause, resume, execute, teardown } = useClientState(
+    args,
+    client,
+    'executeSubscription'
+  );
 
-  const source: Ref<Source<OperationResult<T, V>> | undefined> = ref();
+  const teardownSubscription = watchEffect(onInvalidate => {
+    if (source.value) {
+      fetching.value = true;
 
-  stops.push(
-    watchEffect(() => {
-      updateShallowRef(input, {
-        request: createRequest<T, V>(
-          unref(args.query),
-          unref(args.variables) as V
-        ),
-        isPaused: isPaused.value,
-      });
-    }, watchOptions)
-  );
+      onInvalidate(
+        pipe(
+          source.value,
+          onEnd(() => {
+            fetching.value = false;
+          }),
+          subscribe(result => {
+            fetching.value = true;
+            error.value = result.error;
+            extensions.value = result.extensions;
+            stale.value = !!result.stale;
+            operation.value = result.operation;
 
-  stops.push(
-    watchEffect(() => {
-      source.value = !isPaused.value
-        ? client.value.executeSubscription<T, V>(input.value.request, {
-            ...unref(args.context),
+            if (result.data != null && handler) {
+              const cb = isRef(handler) ? handler.value : handler;
+              if (typeof cb === 'function') {
+                data.value = cb(data.value, result.data);
+                return;
+              }
+            }
+            data.value = result.data as R;
           })
-        : undefined;
-    }, watchOptions)
-  );
-
-  stops.push(
-    watchEffect(onInvalidate => {
-      if (source.value) {
-        fetching.value = true;
+        ).unsubscribe
+      );
+    } else {
+      fetching.value = false;
+    }
+  });
 
-        onInvalidate(
-          pipe(
-            source.value,
-            onEnd(() => {
-              fetching.value = false;
-            }),
-            subscribe(result => {
-              fetching.value = true;
-              data.value =
-                result.data != null
-                  ? typeof scanHandler.value === 'function'
-                    ? scanHandler.value(data.value as any, result.data)
-                    : result.data
-                  : (result.data as any);
-              error.value = result.error;
-              extensions.value = result.extensions;
-              stale.value = !!result.stale;
-              operation.value = result.operation;
-            })
-          ).unsubscribe
-        );
-      } else {
-        fetching.value = false;
-      }
-    }, watchOptions)
-  );
+  stops && stops.push(teardown, teardownSubscription);
 
   const state: UseSubscriptionResponse<T, R, V> = {
     data,
@@ -327,25 +295,14 @@ export function callUseSubscription<
     extensions,
     fetching,
     isPaused,
+    pause,
+    resume,
     executeSubscription(
       opts?: Partial<OperationContext>
     ): UseSubscriptionResponse<T, R, V> {
-      source.value = client.value.executeSubscription<T, V>(
-        input.value.request,
-        {
-          ...unref(args.context),
-          ...opts,
-        }
-      );
-
+      execute(opts);
       return state;
     },
-    pause() {
-      isPaused.value = true;
-    },
-    resume() {
-      isPaused.value = false;
-    },
   };
 
   return state;
diff --git a/packages/vue-urql/src/utils.ts b/packages/vue-urql/src/utils.ts
index a9a83cda..a7806bdd 100644
--- a/packages/vue-urql/src/utils.ts
+++ b/packages/vue-urql/src/utils.ts
@@ -1,45 +1,171 @@
-import type { GraphQLRequest, AnyVariables } from '@urql/core';
-import type { Ref, ShallowRef } from 'vue';
-import { isRef } from 'vue';
+import type {
+  AnyVariables,
+  Client,
+  CombinedError,
+  DocumentInput,
+  Operation,
+  OperationContext,
+  OperationResult,
+  OperationResultSource,
+} from '@urql/core';
+import { createRequest } from '@urql/core';
+import type { Ref } from 'vue';
+import { watchEffect, isReadonly, computed, ref, shallowRef, isRef } from 'vue';
+import type { UseSubscriptionArgs } from './useSubscription';
+import type { UseQueryArgs } from './useQuery';
 
 export type MaybeRef<T> = T | (() => T) | Ref<T>;
 export type MaybeRefObj<T> = T extends {}
   ? { [K in keyof T]: MaybeRef<T[K]> }
   : T;
 
-export const unref = <T>(maybeRef: MaybeRef<T>): T =>
+const unwrap = <T>(maybeRef: MaybeRef<T>): T =>
   typeof maybeRef === 'function'
     ? (maybeRef as () => T)()
     : maybeRef != null && isRef(maybeRef)
     ? maybeRef.value
     : maybeRef;
 
-export interface RequestState<
-  Data = any,
-  Variables extends AnyVariables = AnyVariables,
-> {
-  request: GraphQLRequest<Data, Variables>;
-  isPaused: boolean;
-}
+const isPlainObject = (value: any): boolean => {
+  if (typeof value !== 'object' || value === null) return false;
+  return (
+    value.constructor &&
+    Object.getPrototypeOf(value).constructor === Object.prototype.constructor
+  );
+};
+export const isArray = Array.isArray;
 
-export function createRequestState<
-  Data = any,
-  Variables extends AnyVariables = AnyVariables,
->(
-  request: GraphQLRequest<Data, Variables>,
-  isPaused: boolean
-): RequestState<Data, Variables> {
-  return { request, isPaused };
-}
+const unwrapDeeply = <T>(input: T): T => {
+  input = isRef(input) ? (input.value as T) : input;
 
-export const updateShallowRef = <T extends Record<string, any>>(
-  ref: ShallowRef<T>,
-  next: T
-) => {
-  for (const key in next) {
-    if (ref.value[key] !== next[key]) {
-      ref.value = next;
-      return;
+  if (typeof input === 'function') {
+    return unwrapDeeply(input()) as T;
+  }
+
+  if (input && typeof input === 'object') {
+    if (isArray(input)) {
+      const length = input.length;
+      const out = new Array(length) as T;
+      let i = 0;
+      for (; i < length; i++) {
+        out[i] = unwrapDeeply(input[i]);
+      }
+
+      return out;
+    } else if (isPlainObject(input)) {
+      const keys = Object.keys(input);
+      const length = keys.length;
+      let i = 0;
+      let key: string;
+      const out = {} as T;
+
+      for (; i < length; i++) {
+        key = keys[i];
+        out[key] = unwrapDeeply(input[key]);
+      }
+
+      return out;
     }
   }
+
+  return input;
+};
+
+export const createRequestWithArgs = <
+  T = any,
+  V extends AnyVariables = AnyVariables,
+>(
+  args:
+    | UseQueryArgs<T, V>
+    | UseSubscriptionArgs<T, V>
+    | { query: MaybeRef<DocumentInput<T, V>>; variables: V }
+) => {
+  return createRequest<T, V>(
+    unwrap(args.query),
+    unwrapDeeply(args.variables) as V
+  );
 };
+
+export const useRequestState = <
+  T = any,
+  V extends AnyVariables = AnyVariables,
+>() => {
+  const stale: Ref<boolean> = ref(false);
+  const fetching: Ref<boolean> = ref(false);
+  const error: Ref<CombinedError | undefined> = shallowRef();
+  const operation: Ref<Operation<T, V> | undefined> = shallowRef();
+  const extensions: Ref<Record<string, any> | undefined> = shallowRef();
+  return {
+    stale,
+    fetching,
+    error,
+    operation,
+    extensions,
+  };
+};
+
+export function useClientState<T = any, V extends AnyVariables = AnyVariables>(
+  args: UseQueryArgs<T, V> | UseSubscriptionArgs<T, V>,
+  client: Ref<Client>,
+  method: keyof Pick<Client, 'executeSubscription' | 'executeQuery'>
+) {
+  const source: Ref<OperationResultSource<OperationResult<T, V>> | undefined> =
+    shallowRef();
+
+  const isPaused: Ref<boolean> = isRef(args.pause)
+    ? args.pause
+    : typeof args.pause === 'function'
+    ? computed(args.pause)
+    : ref(!!args.pause);
+
+  const request = computed(() => createRequestWithArgs(args));
+
+  const requestOptions = computed(() => {
+    return 'requestPolicy' in args
+      ? {
+          requestPolicy: unwrap(args.requestPolicy),
+          ...unwrap(args.context),
+        }
+      : {
+          ...unwrap(args.context),
+        };
+  });
+
+  const pause = () => {
+    if (!isReadonly(isPaused)) {
+      isPaused.value = true;
+    }
+  };
+
+  const resume = () => {
+    if (!isReadonly(isPaused)) {
+      isPaused.value = false;
+    }
+  };
+
+  const executeRaw = (opts?: Partial<OperationContext>) => {
+    return client.value[method]<T, V>(request.value, {
+      ...requestOptions.value,
+      ...opts,
+    });
+  };
+
+  const execute = (opts?: Partial<OperationContext>) => {
+    source.value = executeRaw(opts);
+  };
+
+  // it's important to use `watchEffect()` here instead of `watch()`
+  // because it listening for reactive variables inside `executeRaw()` function
+  const teardown = watchEffect(() => {
+    source.value = !isPaused.value ? executeRaw() : undefined;
+  });
+
+  return {
+    source,
+    isPaused,
+    pause,
+    resume,
+    execute,
+    teardown,
+  };
+}


Test patch:
diff --git a/packages/vue-urql/src/useMutation.test.ts b/packages/vue-urql/src/useMutation.test.ts
index 9d4e8ea0..50b79922 100644
--- a/packages/vue-urql/src/useMutation.test.ts
+++ b/packages/vue-urql/src/useMutation.test.ts
@@ -1,5 +1,5 @@
 import { OperationResult, OperationResultSource } from '@urql/core';
-import { reactive } from 'vue';
+import { readonly } from 'vue';
 import { vi, expect, it, beforeEach, describe } from 'vitest';
 
 vi.mock('./useClient.ts', async () => {
@@ -30,15 +30,13 @@ describe('useMutation', () => {
         () => subject.source as OperationResultSource<OperationResult>
       );
 
-    const mutation = reactive(
-      useMutation(gql`
-        mutation {
-          test
-        }
-      `)
-    );
+    const mutation = useMutation(gql`
+      mutation {
+        test
+      }
+    `);
 
-    expect(mutation).toMatchObject({
+    expect(readonly(mutation)).toMatchObject({
       data: undefined,
       stale: false,
       fetching: false,
@@ -50,18 +48,18 @@ describe('useMutation', () => {
 
     const promise = mutation.executeMutation({ test: true });
 
-    expect(mutation.fetching).toBe(true);
-    expect(mutation.stale).toBe(false);
-    expect(mutation.error).toBe(undefined);
+    expect(mutation.fetching.value).toBe(true);
+    expect(mutation.stale.value).toBe(false);
+    expect(mutation.error.value).toBe(undefined);
 
     expect(clientMutation).toHaveBeenCalledTimes(1);
 
     subject.next({ data: { test: true }, stale: false });
-    await promise.then(function () {
-      expect(mutation.fetching).toBe(false);
-      expect(mutation.stale).toBe(false);
-      expect(mutation.error).toBe(undefined);
-      expect(mutation.data).toEqual({ test: true });
-    });
+
+    await promise;
+    expect(mutation.fetching.value).toBe(false);
+    expect(mutation.stale.value).toBe(false);
+    expect(mutation.error.value).toBe(undefined);
+    expect(mutation.data.value).toHaveProperty('test', true);
   });
 });
diff --git a/packages/vue-urql/src/useQuery.test.ts b/packages/vue-urql/src/useQuery.test.ts
index 6ac721cc..a28bf0d6 100644
--- a/packages/vue-urql/src/useQuery.test.ts
+++ b/packages/vue-urql/src/useQuery.test.ts
@@ -1,5 +1,9 @@
-import { OperationResult, OperationResultSource } from '@urql/core';
-import { nextTick, reactive, ref } from 'vue';
+import {
+  OperationResult,
+  OperationResultSource,
+  RequestPolicy,
+} from '@urql/core';
+import { computed, nextTick, reactive, readonly, ref } from 'vue';
 import { vi, expect, it, describe } from 'vitest';
 
 vi.mock('./useClient.ts', async () => ({
@@ -10,10 +14,28 @@ vi.mock('./useClient.ts', async () => ({
 
 import { pipe, makeSubject, fromValue, delay } from 'wonka';
 import { createClient } from '@urql/core';
-import { useQuery } from './useQuery';
+import { useQuery, UseQueryArgs } from './useQuery';
 
 const client = createClient({ url: '/graphql', exchanges: [] });
 
+const createQuery = (args: UseQueryArgs) => {
+  const executeQuery = vi
+    .spyOn(client, 'executeQuery')
+    .mockImplementation(request => {
+      return pipe(
+        fromValue({ operation: request, data: { test: true } }),
+        delay(1)
+      ) as any;
+    });
+
+  const query$ = useQuery(args);
+
+  return {
+    query$,
+    executeQuery,
+  };
+};
+
 describe('useQuery', () => {
   it('runs a query and updates data', async () => {
     const subject = makeSubject<any>();
@@ -23,12 +45,11 @@ describe('useQuery', () => {
         () => subject.source as OperationResultSource<OperationResult>
       );
 
-    const _query = useQuery({
+    const query = useQuery({
       query: `{ test }`,
     });
-    const query = reactive(_query);
 
-    expect(query).toMatchObject({
+    expect(readonly(query)).toMatchObject({
       data: undefined,
       stale: false,
       fetching: true,
@@ -54,12 +75,12 @@ describe('useQuery', () => {
       }
     );
 
-    expect(query.fetching).toBe(true);
+    expect(query.fetching.value).toBe(true);
 
     subject.next({ data: { test: true } });
 
-    expect(query.fetching).toBe(false);
-    expect(query.data).toEqual({ test: true });
+    expect(query.fetching.value).toBe(false);
+    expect(query.data.value).toHaveProperty('test', true);
   });
 
   it('runs queries as a promise-like that resolves when used', async () => {
@@ -79,18 +100,9 @@ describe('useQuery', () => {
   });
 
   it('runs queries as a promise-like that resolves even when the query changes', async () => {
-    const executeQuery = vi
-      .spyOn(client, 'executeQuery')
-      .mockImplementation(request => {
-        return pipe(
-          fromValue({ operation: request, data: { test: true } }),
-          delay(1)
-        ) as any;
-      });
-
     const doc = ref('{ test }');
 
-    const query$ = useQuery({
+    const { executeQuery, query$ } = createQuery({
       query: doc,
     });
 
@@ -108,6 +120,241 @@ describe('useQuery', () => {
     );
   });
 
+  it('runs a query with different variables', async () => {
+    const simpleVariables = {
+      null: null,
+      NaN: NaN,
+      empty: '',
+      bool: false,
+      int: 1,
+      float: 1.1,
+      string: 'string',
+      blob: new Blob(),
+      date: new Date(),
+    };
+
+    const variablesSet = {
+      func: () => 'func',
+      ref: ref('ref'),
+      computed: computed(() => 'computed'),
+      ...simpleVariables,
+    };
+
+    const variablesSetUnwrapped = {
+      func: 'func',
+      ref: 'ref',
+      computed: 'computed',
+      ...simpleVariables,
+    };
+
+    const { query$ } = createQuery({
+      query: ref('{ test }'),
+      variables: {
+        ...variablesSet,
+        nested: variablesSet,
+        array: [variablesSet],
+      },
+    });
+
+    await query$;
+
+    expect(query$.operation.value?.variables).toStrictEqual({
+      ...variablesSetUnwrapped,
+      nested: variablesSetUnwrapped,
+      array: [variablesSetUnwrapped],
+    });
+  });
+
+  it('reacts to ref variables changing', async () => {
+    const variables = ref({ prop: 1 });
+
+    const { executeQuery, query$ } = createQuery({
+      query: ref('{ test }'),
+      variables,
+    });
+
+    await query$;
+    expect(executeQuery).toHaveBeenCalledTimes(1);
+    expect(query$.operation.value).toHaveProperty('variables.prop', 1);
+
+    variables.value.prop++;
+    await query$;
+    expect(executeQuery).toHaveBeenCalledTimes(2);
+    expect(query$.operation.value).toHaveProperty('variables.prop', 2);
+
+    variables.value = { prop: 3 };
+    await query$;
+    expect(executeQuery).toHaveBeenCalledTimes(3);
+    expect(query$.operation.value).toHaveProperty('variables.prop', 3);
+  });
+
+  it('reacts to nested ref variables changing', async () => {
+    const prop = ref(1);
+
+    const { executeQuery, query$ } = createQuery({
+      query: ref('{ test }'),
+      variables: { prop },
+    });
+
+    await query$;
+    expect(executeQuery).toHaveBeenCalledTimes(1);
+    expect(query$.operation.value).toHaveProperty('variables.prop', 1);
+
+    prop.value++;
+    await query$;
+    expect(executeQuery).toHaveBeenCalledTimes(2);
+    expect(query$.operation.value).toHaveProperty('variables.prop', 2);
+  });
+
+  it('reacts to deep nested ref variables changing', async () => {
+    const prop = ref(1);
+
+    const { executeQuery, query$ } = createQuery({
+      query: ref('{ test }'),
+      variables: { deep: { nested: { prop } } },
+    });
+
+    await query$;
+    expect(executeQuery).toHaveBeenCalledTimes(1);
+    expect(query$.operation.value).toHaveProperty(
+      'variables.deep.nested.prop',
+      1
+    );
+
+    prop.value++;
+    await query$;
+    expect(executeQuery).toHaveBeenCalledTimes(2);
+    expect(query$.operation.value).toHaveProperty(
+      'variables.deep.nested.prop',
+      2
+    );
+  });
+
+  it('reacts to reactive variables changing', async () => {
+    const prop = ref(1);
+    const variables = reactive({ prop: 1, deep: { nested: { prop } } });
+
+    const { executeQuery, query$ } = createQuery({
+      query: ref('{ test }'),
+      variables,
+    });
+
+    await query$;
+    expect(executeQuery).toHaveBeenCalledTimes(1);
+    expect(query$.operation.value).toHaveProperty('variables.prop', 1);
+
+    variables.prop++;
+    await query$;
+    expect(executeQuery).toHaveBeenCalledTimes(2);
+    expect(query$.operation.value).toHaveProperty('variables.prop', 2);
+
+    prop.value++;
+    await query$;
+    expect(executeQuery).toHaveBeenCalledTimes(3);
+    expect(query$.operation.value).toHaveProperty(
+      'variables.deep.nested.prop',
+      2
+    );
+  });
+
+  it('reacts to computed variables changing', async () => {
+    const prop = ref(1);
+    const prop2 = ref(1);
+    const variables = computed(() => ({
+      prop: prop.value,
+      deep: { nested: { prop2 } },
+    }));
+
+    const { executeQuery, query$ } = createQuery({
+      query: ref('{ test }'),
+      variables,
+    });
+
+    await query$;
+    expect(executeQuery).toHaveBeenCalledTimes(1);
+    expect(query$.operation.value).toHaveProperty('variables.prop', 1);
+
+    prop.value++;
+    await query$;
+    expect(executeQuery).toHaveBeenCalledTimes(2);
+    expect(query$.operation.value).toHaveProperty('variables.prop', 2);
+
+    prop2.value++;
+    await query$;
+    expect(executeQuery).toHaveBeenCalledTimes(3);
+    expect(query$.operation.value).toHaveProperty(
+      'variables.deep.nested.prop2',
+      2
+    );
+  });
+
+  it('reacts to callback variables changing', async () => {
+    const prop = ref(1);
+    const prop2 = ref(1);
+    const variables = () => ({
+      prop: prop.value,
+      deep: { nested: { prop2 } },
+    });
+
+    const { executeQuery, query$ } = createQuery({
+      query: ref('{ test }'),
+      variables,
+    });
+
+    await query$;
+    expect(executeQuery).toHaveBeenCalledTimes(1);
+    expect(query$.operation.value).toHaveProperty('variables.prop', 1);
+
+    prop.value++;
+    await query$;
+    expect(executeQuery).toHaveBeenCalledTimes(2);
+    expect(query$.operation.value).toHaveProperty('variables.prop', 2);
+
+    prop2.value++;
+    await query$;
+    expect(executeQuery).toHaveBeenCalledTimes(3);
+    expect(query$.operation.value).toHaveProperty(
+      'variables.deep.nested.prop2',
+      2
+    );
+  });
+
+  it('reacts to reactive context argument', async () => {
+    const context = ref<{ requestPolicy: RequestPolicy }>({
+      requestPolicy: 'cache-only',
+    });
+
+    const { executeQuery, query$ } = createQuery({
+      query: ref('{ test }'),
+      context,
+    });
+
+    await query$;
+    expect(executeQuery).toHaveBeenCalledTimes(1);
+
+    context.value.requestPolicy = 'network-only';
+    await query$;
+    expect(executeQuery).toHaveBeenCalledTimes(2);
+  });
+
+  it('reacts to callback context argument', async () => {
+    const requestPolicy = ref<RequestPolicy>('cache-only');
+
+    const { executeQuery, query$ } = createQuery({
+      query: ref('{ test }'),
+      context: () => ({
+        requestPolicy: requestPolicy.value,
+      }),
+    });
+
+    await query$;
+    expect(executeQuery).toHaveBeenCalledTimes(1);
+
+    requestPolicy.value = 'network-only';
+    await query$;
+    expect(executeQuery).toHaveBeenCalledTimes(2);
+  });
+
   it('pauses query when asked to do so', async () => {
     const subject = makeSubject<any>();
     const executeQuery = vi
@@ -116,21 +363,86 @@ describe('useQuery', () => {
         () => subject.source as OperationResultSource<OperationResult>
       );
 
-    const _query = useQuery({
+    const query = useQuery({
       query: `{ test }`,
       pause: true,
     });
-    const query = reactive(_query);
 
     expect(executeQuery).not.toHaveBeenCalled();
 
     query.resume();
     await nextTick();
-    expect(query.fetching).toBe(true);
+    expect(query.fetching.value).toBe(true);
 
     subject.next({ data: { test: true } });
 
-    expect(query.fetching).toBe(false);
-    expect(query.data).toEqual({ test: true });
+    expect(query.fetching.value).toBe(false);
+    expect(query.data.value).toHaveProperty('test', true);
+  });
+
+  it('pauses query with ref variable', async () => {
+    const pause = ref(true);
+
+    const { executeQuery, query$ } = createQuery({
+      query: ref('{ test }'),
+      pause,
+    });
+
+    await query$;
+    expect(executeQuery).not.toHaveBeenCalled();
+
+    pause.value = false;
+    await query$;
+    expect(executeQuery).toHaveBeenCalledTimes(1);
+
+    query$.pause();
+    query$.resume();
+    await query$;
+    expect(executeQuery).toHaveBeenCalledTimes(2);
+  });
+
+  it('pauses query with computed variable', async () => {
+    const pause = ref(true);
+
+    const { executeQuery, query$ } = createQuery({
+      query: ref('{ test }'),
+      pause: computed(() => pause.value),
+    });
+
+    await query$;
+    expect(executeQuery).not.toHaveBeenCalled();
+
+    pause.value = false;
+    await query$;
+    expect(executeQuery).toHaveBeenCalledTimes(1);
+
+    query$.pause();
+    query$.resume();
+    await query$;
+    // this shouldn't be called, as pause/resume functionality should works in sync with passed `pause` variable, e.g.:
+    // if we pass readonly computed variable, then we want to make sure that its value fully controls the state of the request.
+    expect(executeQuery).toHaveBeenCalledTimes(1);
+  });
+
+  it('pauses query with callback', async () => {
+    const pause = ref(true);
+
+    const { executeQuery, query$ } = createQuery({
+      query: ref('{ test }'),
+      pause: () => pause.value,
+    });
+
+    await query$;
+    expect(executeQuery).not.toHaveBeenCalled();
+
+    pause.value = false;
+    await query$;
+    expect(executeQuery).toHaveBeenCalledTimes(1);
+
+    query$.pause();
+    query$.resume();
+    await query$;
+    // the same as computed variable example - user has full control over the request state if using callback
+    expect(executeQuery).toHaveBeenCalledTimes(1);
   });
 });
diff --git a/packages/vue-urql/src/useSubscription.test.ts b/packages/vue-urql/src/useSubscription.test.ts
index 67375b38..232e0ce3 100644
--- a/packages/vue-urql/src/useSubscription.test.ts
+++ b/packages/vue-urql/src/useSubscription.test.ts
@@ -1,7 +1,7 @@
 // @vitest-environment jsdom
 
 import { OperationResult, OperationResultSource } from '@urql/core';
-import { nextTick, reactive, ref } from 'vue';
+import { nextTick, readonly, ref } from 'vue';
 import { vi, expect, it, describe } from 'vitest';
 
 vi.mock('./useClient.ts', async () => ({
@@ -25,13 +25,11 @@ describe('useSubscription', () => {
         () => subject.source as OperationResultSource<OperationResult>
       );
 
-    const sub = reactive(
-      useSubscription({
-        query: `{ test }`,
-      })
-    );
+    const sub = useSubscription({
+      query: `{ test }`,
+    });
 
-    expect(sub).toMatchObject({
+    expect(readonly(sub)).toMatchObject({
       data: undefined,
       stale: false,
       fetching: true,
@@ -53,12 +51,13 @@ describe('useSubscription', () => {
       expect.any(Object)
     );
 
-    expect(sub.fetching).toBe(true);
+    expect(sub.fetching.value).toBe(true);
 
     subject.next({ data: { test: true } });
-    expect(sub.data).toEqual({ test: true });
+    expect(sub.data.value).toHaveProperty('test', true);
+
     subject.complete();
-    expect(sub.fetching).toBe(false);
+    expect(sub.fetching.value).toBe(false);
   });
 
   it('updates the executed subscription when inputs change', async () => {
@@ -70,12 +69,10 @@ describe('useSubscription', () => {
       );
 
     const variables = ref({});
-    const sub = reactive(
-      useSubscription({
-        query: `{ test }`,
-        variables,
-      })
-    );
+    const sub = useSubscription({
+      query: `{ test }`,
+      variables,
+    });
 
     expect(executeSubscription).toHaveBeenCalledWith(
       {
@@ -87,7 +84,7 @@ describe('useSubscription', () => {
     );
 
     subject.next({ data: { test: true } });
-    expect(sub.data).toEqual({ test: true });
+    expect(sub.data.value).toHaveProperty('test', true);
 
     variables.value = { test: true };
     await nextTick();
@@ -101,9 +98,10 @@ describe('useSubscription', () => {
       expect.any(Object)
     );
 
-    expect(sub.fetching).toBe(true);
-    expect(sub.data).toEqual({ test: true });
+    expect(sub.fetching.value).toBe(true);
+    expect(sub.data.value).toHaveProperty('test', true);
   });
+
   it('supports a custom scanning handler', async () => {
     const subject = makeSubject<any>();
     const executeSubscription = vi
@@ -115,13 +113,12 @@ describe('useSubscription', () => {
     const scanHandler = (currentState: any, nextState: any) => ({
       counter: (currentState ? currentState.counter : 0) + nextState.counter,
     });
-    const sub = reactive(
-      useSubscription(
-        {
-          query: `subscription { counter }`,
-        },
-        scanHandler
-      )
+
+    const sub = useSubscription(
+      {
+        query: `subscription { counter }`,
+      },
+      scanHandler
     );
 
     expect(executeSubscription).toHaveBeenCalledWith(
@@ -134,9 +131,9 @@ describe('useSubscription', () => {
     );
 
     subject.next({ data: { counter: 1 } });
-    expect(sub.data).toEqual({ counter: 1 });
+    expect(sub.data.value).toHaveProperty('counter', 1);
 
     subject.next({ data: { counter: 2 } });
-    expect(sub.data).toEqual({ counter: 3 });
+    expect(sub.data.value).toHaveProperty('counter', 3);
   });
 });