Skip to content

Commit f5e371f

Browse files
committed
feat: support effect scope outside of component, fix #1505
1 parent 574bd8f commit f5e371f

File tree

10 files changed

+205
-27
lines changed

10 files changed

+205
-27
lines changed

packages/test-e2e-composable-vue3/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"@vue/apollo-util": "workspace:*",
2121
"graphql": "^16.7.1",
2222
"graphql-tag": "^2.12.6",
23+
"pinia": "^2.1.6",
2324
"test-server": "workspace:*",
2425
"vue": "^3.3.4",
2526
"vue-router": "^4.2.4"
Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,21 @@
1+
<script lang="ts" setup>
2+
import { computed } from 'vue'
3+
import ChannelList from './ChannelList.vue'
4+
import GlobalLoading from './GlobalLoading.vue'
5+
import { useRoute } from 'vue-router'
6+
7+
const route = useRoute()
8+
9+
const displayChannels = computed(() => !route.matched.some(r => r.meta?.layout === 'blank'))
10+
</script>
11+
112
<template>
213
<GlobalLoading />
314
<div class="flex h-screen items-stretch bg-gray-100">
4-
<ChannelList class="w-1/4 border-r border-gray-200" />
15+
<ChannelList
16+
v-if="displayChannels"
17+
class="w-1/4 border-r border-gray-200"
18+
/>
519
<router-view class="flex-1 overflow-auto" />
620
</div>
721
</template>
8-
9-
<script lang="ts">
10-
import { defineComponent } from 'vue'
11-
import ChannelList from './ChannelList.vue'
12-
import GlobalLoading from './GlobalLoading.vue'
13-
14-
export default defineComponent({
15-
name: 'App',
16-
17-
components: {
18-
ChannelList,
19-
GlobalLoading,
20-
},
21-
})
22-
</script>
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<script lang="ts" setup>
2+
import { useChannels } from '@/stores/channel'
3+
4+
const channelStore = useChannels()
5+
</script>
6+
7+
<template>
8+
<div
9+
v-if="channelStore.loading"
10+
class="p-12 text-gray-500"
11+
>
12+
Loading channels...
13+
</div>
14+
15+
<div
16+
v-else
17+
class="flex flex-col bg-white"
18+
>
19+
<router-link
20+
v-for="channel of channelStore.channels"
21+
:key="channel.id"
22+
v-slot="{ href, navigate, isActive }"
23+
:to="{
24+
name: 'channel',
25+
params: {
26+
id: channel.id,
27+
},
28+
}"
29+
custom
30+
>
31+
<a
32+
:href="href"
33+
class="channel-link px-4 py-2 hover:bg-green-100 text-green-700"
34+
:class="{
35+
'bg-green-200 hover:bg-green-300 text-green-900': isActive,
36+
}"
37+
@click="navigate"
38+
>
39+
# {{ channel.label }}
40+
</a>
41+
</router-link>
42+
</div>
43+
</template>
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<script lang="ts" setup>
2+
import { useChannels } from '@/stores/channel'
3+
import { onBeforeUnmount, ref, watch } from 'vue'
4+
5+
const channels = ref<any[]>([])
6+
7+
let unwatch: (() => void) | undefined
8+
setTimeout(() => {
9+
const channelStore = useChannels()
10+
unwatch = watch(() => channelStore.channels, (newChannels) => {
11+
channels.value = newChannels
12+
}, {
13+
immediate: true,
14+
})
15+
}, 0)
16+
17+
onBeforeUnmount(() => {
18+
unwatch?.()
19+
})
20+
</script>
21+
22+
<template>
23+
<div
24+
v-if="channels"
25+
class="flex flex-col bg-white"
26+
>
27+
<router-link
28+
v-for="channel of channels"
29+
:key="channel.id"
30+
v-slot="{ href, navigate, isActive }"
31+
:to="{
32+
name: 'channel',
33+
params: {
34+
id: channel.id,
35+
},
36+
}"
37+
custom
38+
>
39+
<a
40+
:href="href"
41+
class="channel-link px-4 py-2 hover:bg-green-100 text-green-700"
42+
:class="{
43+
'bg-green-200 hover:bg-green-300 text-green-900': isActive,
44+
}"
45+
@click="navigate"
46+
>
47+
# {{ channel.label }}
48+
</a>
49+
</router-link>
50+
</div>
51+
</template>
Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
1-
import { createApp, h, provide } from 'vue'
1+
import { createApp } from 'vue'
22
import { DefaultApolloClient } from '@vue/apollo-composable'
3+
import { createPinia } from 'pinia'
34
import { apolloClient } from './apollo'
45
import App from './components/App.vue'
56
import { router } from './router'
67
import '@/assets/styles/tailwind.css'
78

8-
const app = createApp({
9-
setup () {
10-
provide(DefaultApolloClient, apolloClient)
11-
},
12-
render: () => h(App),
13-
})
9+
const app = createApp(App)
10+
app.use(createPinia())
1411
app.use(router)
12+
app.provide(DefaultApolloClient, apolloClient)
1513
app.mount('#app')

packages/test-e2e-composable-vue3/src/router.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,5 +50,19 @@ export const router = createRouter({
5050
path: '/null-query',
5151
component: () => import('./components/NullQuery.vue'),
5252
},
53+
{
54+
path: '/pinia',
55+
component: () => import('./components/ChannelListPinia.vue'),
56+
meta: {
57+
layout: 'blank',
58+
},
59+
},
60+
{
61+
path: '/pinia2',
62+
component: () => import('./components/ChannelListPinia2.vue'),
63+
meta: {
64+
layout: 'blank',
65+
},
66+
},
5367
],
5468
})
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import gql from 'graphql-tag'
2+
import { useQuery } from '@vue/apollo-composable'
3+
import { defineStore } from 'pinia'
4+
import { computed, watch } from 'vue'
5+
6+
interface Channel {
7+
id: string
8+
label: string
9+
}
10+
11+
export const useChannels = defineStore('channel', () => {
12+
const query = useQuery<{ channels: Channel[] }>(gql`
13+
query channels {
14+
channels {
15+
id
16+
label
17+
}
18+
}
19+
`)
20+
21+
const channels = computed(() => query.result.value?.channels ?? [])
22+
23+
watch(query.loading, value => {
24+
console.log('loading', value)
25+
}, {
26+
immediate: true,
27+
})
28+
29+
return {
30+
loading: query.loading,
31+
channels,
32+
}
33+
})
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
describe('Pinia', () => {
2+
beforeEach(() => {
3+
cy.task('db:reset')
4+
})
5+
6+
it('with current instance', () => {
7+
cy.visit('/pinia')
8+
cy.get('.channel-link').should('have.lengthOf', 2)
9+
cy.contains('.channel-link', '# General')
10+
cy.contains('.channel-link', '# Random')
11+
})
12+
13+
it('with effect scope only', () => {
14+
cy.visit('/pinia2')
15+
cy.get('.channel-link').should('have.lengthOf', 2)
16+
cy.contains('.channel-link', '# General')
17+
cy.contains('.channel-link', '# Random')
18+
})
19+
})

packages/vue-apollo-composable/src/useApolloClient.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { getCurrentInstance, inject } from 'vue-demi'
1+
import { getCurrentInstance, getCurrentScope, inject } from 'vue-demi'
22
import { ApolloClient } from '@apollo/client/core/index.js'
33

44
export const DefaultApolloClient = Symbol('default-apollo-client')
@@ -35,7 +35,7 @@ export function useApolloClient<TCacheShape = any> (clientId?: ClientId): UseApo
3535
// Save current client in current closure scope
3636
const savedCurrentClients = currentApolloClients
3737

38-
if (!getCurrentInstance()) {
38+
if (!getCurrentInstance() && !getCurrentScope()) {
3939
resolveImpl = (id?: ClientId) => {
4040
if (id) {
4141
return resolveClientWithId(savedCurrentClients, id)
@@ -68,7 +68,7 @@ export function useApolloClient<TCacheShape = any> (clientId?: ClientId): UseApo
6868
throw new Error(
6969
`Apollo client with id ${
7070
id ?? 'default'
71-
} not found. Use provideApolloClient() if you are outside of a component setup.`,
71+
} not found. Use an app.runWithContext() or provideApolloClient() if you are outside of a component setup.`,
7272
)
7373
}
7474
return client

pnpm-lock.yaml

Lines changed: 21 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)