Skip to content

Commit e280b63

Browse files
committed
feat: allow app injections in setup stores
Close #1784
1 parent 660e513 commit e280b63

File tree

7 files changed

+71
-32
lines changed

7 files changed

+71
-32
lines changed

packages/pinia/__tests__/storeSetup.spec.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { beforeEach, describe, it, expect, vi } from 'vitest'
22
import { createPinia, defineStore, setActivePinia } from '../src'
3-
import { computed, nextTick, ref, watch } from 'vue'
3+
import { computed, inject, nextTick, ref, watch, version } from 'vue'
44

55
function expectType<T>(_value: T): void {}
66

@@ -132,4 +132,18 @@ describe('store with setup syntax', () => {
132132
expect(store.counter).toBe(2)
133133
expect(counter.value).toBe(2)
134134
})
135+
136+
// TODO:
137+
it.todo('can use app level injections', async () => {
138+
const pinia = createPinia()
139+
setActivePinia(pinia)
140+
const useStore = defineStore('id', () => {
141+
const injected = ref(inject('hello', 'nope'))
142+
143+
return { injected }
144+
})
145+
146+
const store = useStore()
147+
expect(store.injected).toBe('pinia')
148+
})
135149
})

packages/pinia/src/rootStore.ts

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
1-
import {
2-
App,
3-
EffectScope,
4-
getCurrentInstance,
5-
inject,
6-
InjectionKey,
7-
Ref,
8-
} from 'vue-demi'
1+
import { App, EffectScope, inject, InjectionKey, Ref } from 'vue-demi'
2+
// FIXME: move to vue-demi when available
3+
import { hasInjectionContext } from 'vue'
94
import {
105
StateTree,
116
PiniaCustomProperties,
@@ -43,7 +38,7 @@ interface _SetActivePinia {
4338
* Get the currently active pinia if there is any.
4439
*/
4540
export const getActivePinia = () =>
46-
(getCurrentInstance() && inject(piniaSymbol)) || activePinia
41+
(hasInjectionContext() && inject(piniaSymbol)) || activePinia
4742

4843
/**
4944
* Every application must own its own pinia to be able to create stores

packages/pinia/src/store.ts

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ import {
2323
nextTick,
2424
isVue2,
2525
} from 'vue-demi'
26+
// FIXME: move to vue-demi when available
27+
import { hasInjectionContext } from 'vue'
2628
import {
2729
StateTree,
2830
SubscriptionCallback,
@@ -51,6 +53,8 @@ import { IS_CLIENT, USE_DEVTOOLS } from './env'
5153
import { patchObject } from './hmr'
5254
import { addSubscription, triggerSubscriptions, noop } from './subscriptions'
5355

56+
const fallbackRunWithContext = (fn: Function) => fn()
57+
5458
type _ArrayType<AT> = AT extends Array<infer T> ? T : never
5559

5660
function mergeReactiveObjects<
@@ -472,10 +476,13 @@ function createSetupStore<
472476
// creating infinite loops.
473477
pinia._s.set($id, store)
474478

479+
const runWithContext =
480+
(pinia._a && pinia._a.runWithContext) || fallbackRunWithContext
481+
475482
// TODO: idea create skipSerialize that marks properties as non serializable and they are skipped
476483
const setupStore = pinia._e.run(() => {
477484
scope = effectScope()
478-
return scope.run(() => setup())
485+
return runWithContext(() => scope.run(() => setup()))
479486
})!
480487

481488
// overwrite existing actions to support $onAction
@@ -888,12 +895,12 @@ export function defineStore(
888895
}
889896

890897
function useStore(pinia?: Pinia | null, hot?: StoreGeneric): StoreGeneric {
891-
const currentInstance = getCurrentInstance()
898+
const hasContext = hasInjectionContext()
892899
pinia =
893900
// in test mode, ignore the argument provided as we can always retrieve a
894901
// pinia instance with getActivePinia()
895902
(__TEST__ && activePinia && activePinia._testing ? null : pinia) ||
896-
(currentInstance && inject(piniaSymbol, null))
903+
(hasContext ? inject(piniaSymbol, null) : null)
897904
if (pinia) setActivePinia(pinia)
898905

899906
if (__DEV__ && !activePinia) {
@@ -937,18 +944,19 @@ export function defineStore(
937944
pinia._s.delete(hotId)
938945
}
939946

940-
// save stores in instances to access them devtools
941-
if (
942-
__DEV__ &&
943-
IS_CLIENT &&
944-
currentInstance &&
945-
currentInstance.proxy &&
946-
// avoid adding stores that are just built for hot module replacement
947-
!hot
948-
) {
949-
const vm = currentInstance.proxy
950-
const cache = '_pStores' in vm ? vm._pStores! : (vm._pStores = {})
951-
cache[id] = store
947+
if (__DEV__ && IS_CLIENT) {
948+
const currentInstance = getCurrentInstance()
949+
// save stores in instances to access them devtools
950+
if (
951+
currentInstance &&
952+
currentInstance.proxy &&
953+
// avoid adding stores that are just built for hot module replacement
954+
!hot
955+
) {
956+
const vm = currentInstance.proxy
957+
const cache = '_pStores' in vm ? vm._pStores! : (vm._pStores = {})
958+
cache[id] = store
959+
}
952960
}
953961

954962
// StoreGeneric cannot be casted towards Store

packages/playground/src/main.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
import { computed, createApp, markRaw, Ref } from 'vue'
1+
import { computed, createApp, inject, markRaw, Ref } from 'vue'
22
import App from './App.vue'
33
import { createPinia } from 'pinia'
44
import { router } from './router'
55
import {
66
RouteLocationNormalized,
77
RouteLocationNormalizedLoaded,
88
} from 'vue-router'
9+
import { useCounter } from './stores/counterSetup'
910

1011
const pinia = createPinia()
1112

@@ -60,4 +61,13 @@ if (import.meta.hot) {
6061
// }
6162
}
6263

63-
createApp(App).use(router).use(pinia).mount('#app')
64+
const app = createApp(App).use(pinia).use(router).provide('hello', 'injections')
65+
66+
app.mount('#app')
67+
68+
console.log(
69+
'hello',
70+
app.runWithContext(() => inject('hello'))
71+
)
72+
73+
const store = useCounter()

packages/playground/src/stores/counterSetup.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import { computed, toRefs, reactive } from 'vue'
1+
import { computed, toRefs, reactive, inject } from 'vue'
22
import { acceptHMRUpdate, defineStore } from 'pinia'
3+
import { useRoute } from 'vue-router'
34

45
const delay = (t: number) => new Promise((r) => setTimeout(r, t))
56

@@ -11,6 +12,11 @@ export const useCounter = defineStore('counter-setup', () => {
1112
numbers: [] as number[],
1213
})
1314

15+
const route = useRoute()
16+
console.log('route in setup', route)
17+
18+
console.log('injection', inject('hello'))
19+
1420
const double = computed(() => state.n * 2)
1521

1622
function increment(amount = 1) {

packages/playground/tsconfig.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@
1010
"resolveJsonModule": true,
1111
"esModuleInterop": true,
1212
"lib": ["esnext", "dom"],
13-
"types": ["vite/client"]
14-
}
13+
"types": ["vite/client"],
14+
"paths": {
15+
"pinia": ["../pinia/src/index.ts"]
16+
}
17+
},
1518
// "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
1619
}

packages/playground/vite.config.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,18 @@ import path from 'path'
77
export default defineConfig({
88
plugins: [vue(), copyPiniaPlugin()],
99
define: {
10-
// __DEV__: 'true',
10+
__DEV__: 'true',
1111
// __BROWSER__: 'true',
1212
__TEST__: 'false',
1313
},
1414
resolve: {
1515
// alias: {
1616
// '@vue/composition-api': 'vue-demi',
1717
// },
18-
dedupe: ['vue-demi', 'vue'],
18+
dedupe: ['vue-demi', 'vue', 'pinia'],
19+
alias: {
20+
pinia: path.resolve(__dirname, '../pinia/src/index.ts'),
21+
},
1922
},
2023
optimizeDeps: {
2124
exclude: ['vue-demi', '@vueuse/shared', '@vueuse/core', 'pinia'],

0 commit comments

Comments
 (0)