feat: support effect scope outside of component, fix #1505
This commit is contained in:
@@ -20,6 +20,7 @@
|
||||
"@vue/apollo-util": "workspace:*",
|
||||
"graphql": "^16.7.1",
|
||||
"graphql-tag": "^2.12.6",
|
||||
"pinia": "^2.1.6",
|
||||
"test-server": "workspace:*",
|
||||
"vue": "^3.3.4",
|
||||
"vue-router": "^4.2.4"
|
||||
|
||||
@@ -1,22 +1,21 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue'
|
||||
import ChannelList from './ChannelList.vue'
|
||||
import GlobalLoading from './GlobalLoading.vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
const displayChannels = computed(() => !route.matched.some(r => r.meta?.layout === 'blank'))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<GlobalLoading />
|
||||
<div class="flex h-screen items-stretch bg-gray-100">
|
||||
<ChannelList class="w-1/4 border-r border-gray-200" />
|
||||
<ChannelList
|
||||
v-if="displayChannels"
|
||||
class="w-1/4 border-r border-gray-200"
|
||||
/>
|
||||
<router-view class="flex-1 overflow-auto" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
import ChannelList from './ChannelList.vue'
|
||||
import GlobalLoading from './GlobalLoading.vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'App',
|
||||
|
||||
components: {
|
||||
ChannelList,
|
||||
GlobalLoading,
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
<script lang="ts" setup>
|
||||
import { useChannels } from '@/stores/channel'
|
||||
|
||||
const channelStore = useChannels()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
v-if="channelStore.loading"
|
||||
class="p-12 text-gray-500"
|
||||
>
|
||||
Loading channels...
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-else
|
||||
class="flex flex-col bg-white"
|
||||
>
|
||||
<router-link
|
||||
v-for="channel of channelStore.channels"
|
||||
:key="channel.id"
|
||||
v-slot="{ href, navigate, isActive }"
|
||||
:to="{
|
||||
name: 'channel',
|
||||
params: {
|
||||
id: channel.id,
|
||||
},
|
||||
}"
|
||||
custom
|
||||
>
|
||||
<a
|
||||
:href="href"
|
||||
class="channel-link px-4 py-2 hover:bg-green-100 text-green-700"
|
||||
:class="{
|
||||
'bg-green-200 hover:bg-green-300 text-green-900': isActive,
|
||||
}"
|
||||
@click="navigate"
|
||||
>
|
||||
# {{ channel.label }}
|
||||
</a>
|
||||
</router-link>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,51 @@
|
||||
<script lang="ts" setup>
|
||||
import { useChannels } from '@/stores/channel'
|
||||
import { onBeforeUnmount, ref, watch } from 'vue'
|
||||
|
||||
const channels = ref<any[]>([])
|
||||
|
||||
let unwatch: (() => void) | undefined
|
||||
setTimeout(() => {
|
||||
const channelStore = useChannels()
|
||||
unwatch = watch(() => channelStore.channels, (newChannels) => {
|
||||
channels.value = newChannels
|
||||
}, {
|
||||
immediate: true,
|
||||
})
|
||||
}, 0)
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
unwatch?.()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
v-if="channels"
|
||||
class="flex flex-col bg-white"
|
||||
>
|
||||
<router-link
|
||||
v-for="channel of channels"
|
||||
:key="channel.id"
|
||||
v-slot="{ href, navigate, isActive }"
|
||||
:to="{
|
||||
name: 'channel',
|
||||
params: {
|
||||
id: channel.id,
|
||||
},
|
||||
}"
|
||||
custom
|
||||
>
|
||||
<a
|
||||
:href="href"
|
||||
class="channel-link px-4 py-2 hover:bg-green-100 text-green-700"
|
||||
:class="{
|
||||
'bg-green-200 hover:bg-green-300 text-green-900': isActive,
|
||||
}"
|
||||
@click="navigate"
|
||||
>
|
||||
# {{ channel.label }}
|
||||
</a>
|
||||
</router-link>
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,15 +1,13 @@
|
||||
import { createApp, h, provide } from 'vue'
|
||||
import { createApp } from 'vue'
|
||||
import { DefaultApolloClient } from '@vue/apollo-composable'
|
||||
import { createPinia } from 'pinia'
|
||||
import { apolloClient } from './apollo'
|
||||
import App from './components/App.vue'
|
||||
import { router } from './router'
|
||||
import '@/assets/styles/tailwind.css'
|
||||
|
||||
const app = createApp({
|
||||
setup () {
|
||||
provide(DefaultApolloClient, apolloClient)
|
||||
},
|
||||
render: () => h(App),
|
||||
})
|
||||
const app = createApp(App)
|
||||
app.use(createPinia())
|
||||
app.use(router)
|
||||
app.provide(DefaultApolloClient, apolloClient)
|
||||
app.mount('#app')
|
||||
|
||||
@@ -50,5 +50,19 @@ export const router = createRouter({
|
||||
path: '/null-query',
|
||||
component: () => import('./components/NullQuery.vue'),
|
||||
},
|
||||
{
|
||||
path: '/pinia',
|
||||
component: () => import('./components/ChannelListPinia.vue'),
|
||||
meta: {
|
||||
layout: 'blank',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/pinia2',
|
||||
component: () => import('./components/ChannelListPinia2.vue'),
|
||||
meta: {
|
||||
layout: 'blank',
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import gql from 'graphql-tag'
|
||||
import { useQuery } from '@vue/apollo-composable'
|
||||
import { defineStore } from 'pinia'
|
||||
import { computed, watch } from 'vue'
|
||||
|
||||
interface Channel {
|
||||
id: string
|
||||
label: string
|
||||
}
|
||||
|
||||
export const useChannels = defineStore('channel', () => {
|
||||
const query = useQuery<{ channels: Channel[] }>(gql`
|
||||
query channels {
|
||||
channels {
|
||||
id
|
||||
label
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
const channels = computed(() => query.result.value?.channels ?? [])
|
||||
|
||||
watch(query.loading, value => {
|
||||
console.log('loading', value)
|
||||
}, {
|
||||
immediate: true,
|
||||
})
|
||||
|
||||
return {
|
||||
loading: query.loading,
|
||||
channels,
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,19 @@
|
||||
describe('Pinia', () => {
|
||||
beforeEach(() => {
|
||||
cy.task('db:reset')
|
||||
})
|
||||
|
||||
it('with current instance', () => {
|
||||
cy.visit('/pinia')
|
||||
cy.get('.channel-link').should('have.lengthOf', 2)
|
||||
cy.contains('.channel-link', '# General')
|
||||
cy.contains('.channel-link', '# Random')
|
||||
})
|
||||
|
||||
it('with effect scope only', () => {
|
||||
cy.visit('/pinia2')
|
||||
cy.get('.channel-link').should('have.lengthOf', 2)
|
||||
cy.contains('.channel-link', '# General')
|
||||
cy.contains('.channel-link', '# Random')
|
||||
})
|
||||
})
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getCurrentInstance, inject } from 'vue-demi'
|
||||
import { getCurrentInstance, getCurrentScope, inject } from 'vue-demi'
|
||||
import { ApolloClient } from '@apollo/client/core/index.js'
|
||||
|
||||
export const DefaultApolloClient = Symbol('default-apollo-client')
|
||||
@@ -35,7 +35,7 @@ export function useApolloClient<TCacheShape = any> (clientId?: ClientId): UseApo
|
||||
// Save current client in current closure scope
|
||||
const savedCurrentClients = currentApolloClients
|
||||
|
||||
if (!getCurrentInstance()) {
|
||||
if (!getCurrentInstance() && !getCurrentScope()) {
|
||||
resolveImpl = (id?: ClientId) => {
|
||||
if (id) {
|
||||
return resolveClientWithId(savedCurrentClients, id)
|
||||
@@ -68,7 +68,7 @@ export function useApolloClient<TCacheShape = any> (clientId?: ClientId): UseApo
|
||||
throw new Error(
|
||||
`Apollo client with id ${
|
||||
id ?? 'default'
|
||||
} not found. Use provideApolloClient() if you are outside of a component setup.`,
|
||||
} not found. Use an app.runWithContext() or provideApolloClient() if you are outside of a component setup.`,
|
||||
)
|
||||
}
|
||||
return client
|
||||
|
||||
Generated
+21
-1
@@ -189,6 +189,9 @@ importers:
|
||||
graphql-tag:
|
||||
specifier: ^2.12.6
|
||||
version: 2.12.6(graphql@16.7.1)
|
||||
pinia:
|
||||
specifier: ^2.1.6
|
||||
version: 2.1.6(typescript@5.0.2)(vue@3.3.4)
|
||||
test-server:
|
||||
specifier: workspace:*
|
||||
version: link:../test-server
|
||||
@@ -12834,6 +12837,24 @@ packages:
|
||||
engines: {node: '>=6'}
|
||||
dev: true
|
||||
|
||||
/pinia@2.1.6(typescript@5.0.2)(vue@3.3.4):
|
||||
resolution: {integrity: sha512-bIU6QuE5qZviMmct5XwCesXelb5VavdOWKWaB17ggk++NUwQWWbP5YnsONTk3b752QkW9sACiR81rorpeOMSvQ==}
|
||||
peerDependencies:
|
||||
'@vue/composition-api': ^1.4.0
|
||||
typescript: '>=4.4.4'
|
||||
vue: '*'
|
||||
peerDependenciesMeta:
|
||||
'@vue/composition-api':
|
||||
optional: true
|
||||
typescript:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@vue/devtools-api': 6.5.0
|
||||
typescript: 5.0.2
|
||||
vue: 3.3.4
|
||||
vue-demi: 0.14.6(@vue/composition-api@1.0.0)(vue@3.3.4)
|
||||
dev: false
|
||||
|
||||
/pirates@4.0.5:
|
||||
resolution: {integrity: sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==}
|
||||
engines: {node: '>= 6'}
|
||||
@@ -15560,7 +15581,6 @@ packages:
|
||||
resolution: {integrity: sha512-wVORMBGO/FAs/++blGNeAVdbNKtIh1rbBL2EyQ1+J9lClJ93KiiKe8PmFIVdXhHcyv44SL9oglmfeSsndo0jRw==}
|
||||
engines: {node: '>=12.20'}
|
||||
hasBin: true
|
||||
dev: true
|
||||
|
||||
/uglify-es@3.3.9:
|
||||
resolution: {integrity: sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==}
|
||||
|
||||
Reference in New Issue
Block a user