Skip to content

Commit 230febc

Browse files
authored
Use getCurrentScope instead of getCurrentInstance (#3806)
Signed-off-by: Julien Hauseux <julien.hauseux@gmail.com>
1 parent b757332 commit 230febc

4 files changed

Lines changed: 96 additions & 30 deletions

File tree

.changeset/thick-roses-joke.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@urql/vue': major
3+
---
4+
5+
Bump Vue to 3.2+ and replace getCurrentInstance with getCurrentScope

packages/vue-urql/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
},
5757
"peerDependencies": {
5858
"@urql/core": "^5.0.0",
59-
"vue": "^2.7.0 || ^3.0.0"
59+
"vue": "^3.2.0"
6060
},
6161
"dependencies": {
6262
"@urql/core": "workspace:^5.2.0",

packages/vue-urql/src/useClient.test.ts

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
// @vitest-environment jsdom
22

33
import { expect, it, describe } from 'vitest';
4-
import { defineComponent } from 'vue';
4+
import { defineComponent, effectScope, h } from 'vue';
55
import { mount } from '@vue/test-utils';
66
import { Client } from '@urql/core';
77
import { useClient, provideClient } from './useClient';
88

9-
describe('provideClient', () => {
9+
describe('provideClient and useClient', () => {
1010
it('provides client to current component instance', async () => {
1111
const TestComponent = defineComponent({
1212
setup() {
@@ -25,4 +25,66 @@ describe('provideClient', () => {
2525

2626
mount(TestComponent);
2727
});
28+
29+
it('provides client to child components via provide/inject', async () => {
30+
const ChildComponent = defineComponent({
31+
setup() {
32+
const client = useClient();
33+
expect(client).toBeDefined();
34+
return () => null;
35+
},
36+
});
37+
38+
const ParentComponent = defineComponent({
39+
components: { ChildComponent },
40+
setup() {
41+
provideClient(
42+
new Client({
43+
url: 'test',
44+
exchanges: [],
45+
})
46+
);
47+
return () => h(ChildComponent);
48+
},
49+
});
50+
51+
mount(ParentComponent);
52+
});
53+
54+
it('works in effect scopes outside components', () => {
55+
const scope = effectScope();
56+
57+
scope.run(() => {
58+
provideClient(
59+
new Client({
60+
url: 'test',
61+
exchanges: [],
62+
})
63+
);
64+
65+
const client = useClient();
66+
expect(client).toBeDefined();
67+
});
68+
});
69+
70+
it('throws error when no client is provided', () => {
71+
expect(() => {
72+
const TestComponent = defineComponent({
73+
setup() {
74+
// No provideClient called
75+
useClient(); // Should throw
76+
return null;
77+
},
78+
});
79+
80+
mount(TestComponent);
81+
}).toThrow('No urql Client was provided');
82+
});
83+
84+
it('throws error when called outside reactive context', () => {
85+
expect(() => {
86+
// Called outside any component or scope
87+
useClient();
88+
}).toThrow('reactive context');
89+
});
2890
});

packages/vue-urql/src/useClient.ts

Lines changed: 26 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
import type { App, Ref } from 'vue';
2-
import { getCurrentInstance, inject, provide, isRef, shallowRef } from 'vue';
1+
import { type App, getCurrentScope, type Ref } from 'vue';
2+
import { inject, provide, isRef, shallowRef } from 'vue';
33
import type { ClientOptions } from '@urql/core';
44
import { Client } from '@urql/core';
55

6-
const clientsPerInstance = new WeakMap<{}, Ref<Client>>();
6+
// WeakMap to store client instances as fallback when client is provided and used in the same component
7+
const clientsPerScope = new WeakMap<{}, Ref<Client>>();
78

8-
/** Provides a {@link Client} to a component’s children.
9+
/** Provides a {@link Client} to a component and it’s children.
910
*
1011
* @param opts - {@link ClientOptions}, a {@link Client}, or a reactive ref object of a `Client`.
1112
*
@@ -19,18 +20,16 @@ const clientsPerInstance = new WeakMap<{}, Ref<Client>>();
1920
*
2021
* @example
2122
* ```ts
22-
* import { provideClient } from '@urql/vue';
23-
* // All of `@urql/core` is also re-exported by `@urql/vue`:
24-
* import { Client, cacheExchange, fetchExchange } from '@urql/core';
23+
* <script setup>
24+
* import { provideClient } from '@urql/vue';
25+
* // All of `@urql/core` is also re-exported by `@urql/vue`:
26+
* import { Client, cacheExchange, fetchExchange } from '@urql/core';
2527
*
26-
* export default {
27-
* setup() {
28-
* provideClient(new Client({
29-
* url: 'https://API',
30-
* exchanges: [cacheExchange, fetchExchange],
31-
* }));
32-
* },
33-
* };
28+
* provideClient(new Client({
29+
* url: 'https://API',
30+
* exchanges: [cacheExchange, fetchExchange],
31+
* }));
32+
* </script>
3433
* ```
3534
*/
3635
export function provideClient(opts: ClientOptions | Client | Ref<Client>) {
@@ -41,9 +40,9 @@ export function provideClient(opts: ClientOptions | Client | Ref<Client>) {
4140
client = opts;
4241
}
4342

44-
const instance = getCurrentInstance();
45-
if (instance) {
46-
clientsPerInstance.set(instance, client);
43+
const scope = getCurrentScope();
44+
if (scope) {
45+
clientsPerScope.set(scope, client);
4746
}
4847

4948
provide('$urql', client);
@@ -88,26 +87,26 @@ export function install(app: App, opts: ClientOptions | Client | Ref<Client>) {
8887
/** Returns a provided reactive ref object of a {@link Client}.
8988
*
9089
* @remarks
91-
* `useClient` may be called in Vue `setup` functions to retrieve a
92-
* reactive rev object of a {@link Client} that’s previously been
90+
* `useClient` may be called in a reactive context to retrieve a
91+
* reactive ref object of a {@link Client} that’s previously been
9392
* provided with {@link provideClient} in the current or a parent’s
9493
* `setup` function.
9594
*
9695
* @throws
97-
* In development, if `useClient` is called outside of a Vue `setup`
98-
* function or no {@link Client} was provided, an error will be thrown.
96+
* In development, if `useClient` is called outside of a reactive context
97+
* or no {@link Client} was provided, an error will be thrown.
9998
*/
10099
export function useClient(): Ref<Client> {
101-
const instance = getCurrentInstance();
102-
if (process.env.NODE_ENV !== 'production' && !instance) {
100+
const scope = getCurrentScope();
101+
if (process.env.NODE_ENV !== 'production' && !scope) {
103102
throw new Error(
104-
'use* functions may only be called during the `setup()` or other lifecycle hooks.'
103+
'use* function must be called within a reactive context (component setup, composable, or effect scope).'
105104
);
106105
}
107106

108107
let client = inject('$urql') as Ref<Client> | undefined;
109-
if (!client && instance) {
110-
client = clientsPerInstance.get(instance);
108+
if (!client) {
109+
client = clientsPerScope.get(scope!);
111110
}
112111

113112
if (process.env.NODE_ENV !== 'production' && !client) {

0 commit comments

Comments
 (0)