Skip to content

Commit

Permalink
feat: allow app injections in setup stores
Browse files Browse the repository at this point in the history
Close #1784
  • Loading branch information
posva committed May 17, 2023
1 parent 660e513 commit e280b63
Show file tree
Hide file tree
Showing 7 changed files with 71 additions and 32 deletions.
16 changes: 15 additions & 1 deletion packages/pinia/__tests__/storeSetup.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { beforeEach, describe, it, expect, vi } from 'vitest'
import { createPinia, defineStore, setActivePinia } from '../src'
import { computed, nextTick, ref, watch } from 'vue'
import { computed, inject, nextTick, ref, watch, version } from 'vue'

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

Expand Down Expand Up @@ -132,4 +132,18 @@ describe('store with setup syntax', () => {
expect(store.counter).toBe(2)
expect(counter.value).toBe(2)
})

// TODO:
it.todo('can use app level injections', async () => {
const pinia = createPinia()
setActivePinia(pinia)
const useStore = defineStore('id', () => {
const injected = ref(inject('hello', 'nope'))

return { injected }
})

const store = useStore()
expect(store.injected).toBe('pinia')
})
})
13 changes: 4 additions & 9 deletions packages/pinia/src/rootStore.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
import {
App,
EffectScope,
getCurrentInstance,
inject,
InjectionKey,
Ref,
} from 'vue-demi'
import { App, EffectScope, inject, InjectionKey, Ref } from 'vue-demi'
// FIXME: move to vue-demi when available
import { hasInjectionContext } from 'vue'
import {
StateTree,
PiniaCustomProperties,
Expand Down Expand Up @@ -43,7 +38,7 @@ interface _SetActivePinia {
* Get the currently active pinia if there is any.
*/
export const getActivePinia = () =>
(getCurrentInstance() && inject(piniaSymbol)) || activePinia
(hasInjectionContext() && inject(piniaSymbol)) || activePinia

/**
* Every application must own its own pinia to be able to create stores
Expand Down
38 changes: 23 additions & 15 deletions packages/pinia/src/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import {
nextTick,
isVue2,
} from 'vue-demi'
// FIXME: move to vue-demi when available
import { hasInjectionContext } from 'vue'
import {
StateTree,
SubscriptionCallback,
Expand Down Expand Up @@ -51,6 +53,8 @@ import { IS_CLIENT, USE_DEVTOOLS } from './env'
import { patchObject } from './hmr'
import { addSubscription, triggerSubscriptions, noop } from './subscriptions'

const fallbackRunWithContext = (fn: Function) => fn()

type _ArrayType<AT> = AT extends Array<infer T> ? T : never

function mergeReactiveObjects<
Expand Down Expand Up @@ -472,10 +476,13 @@ function createSetupStore<
// creating infinite loops.
pinia._s.set($id, store)

const runWithContext =
(pinia._a && pinia._a.runWithContext) || fallbackRunWithContext

// TODO: idea create skipSerialize that marks properties as non serializable and they are skipped
const setupStore = pinia._e.run(() => {
scope = effectScope()
return scope.run(() => setup())
return runWithContext(() => scope.run(() => setup()))
})!

// overwrite existing actions to support $onAction
Expand Down Expand Up @@ -888,12 +895,12 @@ export function defineStore(
}

function useStore(pinia?: Pinia | null, hot?: StoreGeneric): StoreGeneric {
const currentInstance = getCurrentInstance()
const hasContext = hasInjectionContext()
pinia =
// in test mode, ignore the argument provided as we can always retrieve a
// pinia instance with getActivePinia()
(__TEST__ && activePinia && activePinia._testing ? null : pinia) ||
(currentInstance && inject(piniaSymbol, null))
(hasContext ? inject(piniaSymbol, null) : null)
if (pinia) setActivePinia(pinia)

if (__DEV__ && !activePinia) {
Expand Down Expand Up @@ -937,18 +944,19 @@ export function defineStore(
pinia._s.delete(hotId)
}

// save stores in instances to access them devtools
if (
__DEV__ &&
IS_CLIENT &&
currentInstance &&
currentInstance.proxy &&
// avoid adding stores that are just built for hot module replacement
!hot
) {
const vm = currentInstance.proxy
const cache = '_pStores' in vm ? vm._pStores! : (vm._pStores = {})
cache[id] = store
if (__DEV__ && IS_CLIENT) {
const currentInstance = getCurrentInstance()
// save stores in instances to access them devtools
if (
currentInstance &&
currentInstance.proxy &&
// avoid adding stores that are just built for hot module replacement
!hot
) {
const vm = currentInstance.proxy
const cache = '_pStores' in vm ? vm._pStores! : (vm._pStores = {})
cache[id] = store
}
}

// StoreGeneric cannot be casted towards Store
Expand Down
14 changes: 12 additions & 2 deletions packages/playground/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { computed, createApp, markRaw, Ref } from 'vue'
import { computed, createApp, inject, markRaw, Ref } from 'vue'
import App from './App.vue'
import { createPinia } from 'pinia'
import { router } from './router'
import {
RouteLocationNormalized,
RouteLocationNormalizedLoaded,
} from 'vue-router'
import { useCounter } from './stores/counterSetup'

const pinia = createPinia()

Expand Down Expand Up @@ -60,4 +61,13 @@ if (import.meta.hot) {
// }
}

createApp(App).use(router).use(pinia).mount('#app')
const app = createApp(App).use(pinia).use(router).provide('hello', 'injections')

app.mount('#app')

console.log(
'hello',
app.runWithContext(() => inject('hello'))
)

const store = useCounter()
8 changes: 7 additions & 1 deletion packages/playground/src/stores/counterSetup.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { computed, toRefs, reactive } from 'vue'
import { computed, toRefs, reactive, inject } from 'vue'
import { acceptHMRUpdate, defineStore } from 'pinia'
import { useRoute } from 'vue-router'

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

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

const route = useRoute()
console.log('route in setup', route)

console.log('injection', inject('hello'))

const double = computed(() => state.n * 2)

function increment(amount = 1) {
Expand Down
7 changes: 5 additions & 2 deletions packages/playground/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@
"resolveJsonModule": true,
"esModuleInterop": true,
"lib": ["esnext", "dom"],
"types": ["vite/client"]
}
"types": ["vite/client"],
"paths": {
"pinia": ["../pinia/src/index.ts"]
}
},
// "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
}
7 changes: 5 additions & 2 deletions packages/playground/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,18 @@ import path from 'path'
export default defineConfig({
plugins: [vue(), copyPiniaPlugin()],
define: {
// __DEV__: 'true',
__DEV__: 'true',
// __BROWSER__: 'true',
__TEST__: 'false',
},
resolve: {
// alias: {
// '@vue/composition-api': 'vue-demi',
// },
dedupe: ['vue-demi', 'vue'],
dedupe: ['vue-demi', 'vue', 'pinia'],
alias: {
pinia: path.resolve(__dirname, '../pinia/src/index.ts'),
},
},
optimizeDeps: {
exclude: ['vue-demi', '@vueuse/shared', '@vueuse/core', 'pinia'],
Expand Down

0 comments on commit e280b63

Please sign in to comment.