Mirror: The highly customizable and versatile GraphQL client with which you add on features like normalized caching as you grow.

Use getCurrentScope instead of getCurrentInstance (#3806)

Signed-off-by: Julien Hauseux <julien.hauseux@gmail.com>

authored by

Julien Hauseux and committed by
GitHub
230febcc b757332b

+96 -30
+5
.changeset/thick-roses-joke.md
··· 1 + --- 2 + '@urql/vue': major 3 + --- 4 + 5 + Bump Vue to 3.2+ and replace getCurrentInstance with getCurrentScope
+1 -1
packages/vue-urql/package.json
··· 56 56 }, 57 57 "peerDependencies": { 58 58 "@urql/core": "^5.0.0", 59 - "vue": "^2.7.0 || ^3.0.0" 59 + "vue": "^3.2.0" 60 60 }, 61 61 "dependencies": { 62 62 "@urql/core": "workspace:^5.2.0",
+64 -2
packages/vue-urql/src/useClient.test.ts
··· 1 1 // @vitest-environment jsdom 2 2 3 3 import { expect, it, describe } from 'vitest'; 4 - import { defineComponent } from 'vue'; 4 + import { defineComponent, effectScope, h } from 'vue'; 5 5 import { mount } from '@vue/test-utils'; 6 6 import { Client } from '@urql/core'; 7 7 import { useClient, provideClient } from './useClient'; 8 8 9 - describe('provideClient', () => { 9 + describe('provideClient and useClient', () => { 10 10 it('provides client to current component instance', async () => { 11 11 const TestComponent = defineComponent({ 12 12 setup() { ··· 24 24 }); 25 25 26 26 mount(TestComponent); 27 + }); 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'); 27 89 }); 28 90 });
+26 -27
packages/vue-urql/src/useClient.ts
··· 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'; 3 3 import type { ClientOptions } from '@urql/core'; 4 4 import { Client } from '@urql/core'; 5 5 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>>(); 7 8 8 - /** Provides a {@link Client} to a component’s children. 9 + /** Provides a {@link Client} to a component and it’s children. 9 10 * 10 11 * @param opts - {@link ClientOptions}, a {@link Client}, or a reactive ref object of a `Client`. 11 12 * ··· 19 20 * 20 21 * @example 21 22 * ```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'; 25 27 * 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> 34 33 * ``` 35 34 */ 36 35 export function provideClient(opts: ClientOptions | Client | Ref<Client>) { ··· 41 40 client = opts; 42 41 } 43 42 44 - const instance = getCurrentInstance(); 45 - if (instance) { 46 - clientsPerInstance.set(instance, client); 43 + const scope = getCurrentScope(); 44 + if (scope) { 45 + clientsPerScope.set(scope, client); 47 46 } 48 47 49 48 provide('$urql', client); ··· 88 87 /** Returns a provided reactive ref object of a {@link Client}. 89 88 * 90 89 * @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 93 92 * provided with {@link provideClient} in the current or a parent’s 94 93 * `setup` function. 95 94 * 96 95 * @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. 99 98 */ 100 99 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) { 103 102 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).' 105 104 ); 106 105 } 107 106 108 107 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!); 111 110 } 112 111 113 112 if (process.env.NODE_ENV !== 'production' && !client) {