import { createPinia, defineStore, setActivePinia, Pinia } from '../src'
import { mount } from '@vue/test-utils'
-import { getCurrentInstance, nextTick, watch } from 'vue'
+import { defineComponent, getCurrentInstance, nextTick, watch } from 'vue'
describe('Store', () => {
let pinia: Pinia
wrapper.unmount()
})
+
+ it('reuses stores from parent components', () => {
+ let s1, s2
+ const useStore = defineStore({ id: 'one' })
+ const pinia = createPinia()
+
+ const Child = defineComponent({
+ setup() {
+ s2 = useStore()
+ },
+ template: `child`,
+ })
+
+ mount(
+ {
+ setup() {
+ s1 = useStore()
+ return { s1 }
+ },
+ components: { Child },
+ template: `<child/>`,
+ },
+ { global: { plugins: [pinia] } }
+ )
+
+ expect(s1).toBeDefined()
+ expect(s1).toBe(s2)
+ })
})
getCurrentInstance,
reactive,
onUnmounted,
+ InjectionKey,
+ provide,
} from 'vue'
import {
StateTree,
Method,
DefineStoreOptions,
StoreDefinition,
+ GenericStore,
} from './types'
import {
getActivePinia,
$id: Id,
buildState: () => S = () => ({} as S),
initialState?: S | undefined
-): [StoreWithState<Id, S>, { get: () => S; set: (newValue: S) => void }] {
+): [
+ StoreWithState<Id, S>,
+ { get: () => S; set: (newValue: S) => void },
+ InjectionKey<GenericStore>
+] {
const pinia = getActivePinia()
pinia.state.value[$id] = initialState || buildState()
// const state: Ref<S> = toRef(_p.state.value, $id)
$reset,
} as StoreWithState<Id, S>
+ const injectionSymbol = __DEV__ ? Symbol(`PiniaStore(${$id})`) : Symbol()
+
+ // avoid warnings with injections not found
+ if (pinia._a) {
+ pinia._a.provide(injectionSymbol, null)
+ }
+
return [
storeWithState,
{
isListening = true
},
},
+ injectionSymbol,
]
}
const { id, state, getters, actions } = options
function useStore(pinia?: Pinia | null): Store<Id, S, G, A> {
+ const hasInstance = getCurrentInstance()
+ // only run provide when pinia hasn't been manually passed
+ const shouldProvide = hasInstance && !pinia
// avoid injecting if `useStore` when not possible
- pinia = pinia || (getCurrentInstance() && inject(piniaSymbol))
+ pinia = pinia || (hasInstance && inject(piniaSymbol))
if (pinia) setActivePinia(pinia)
// TODO: worth warning on server if no piniaKey as it can leak data
pinia = getActivePinia()
if (!stores) storesMap.set(pinia, (stores = new Map()))
let storeAndDescriptor = stores.get(id) as
- | [StoreWithState<Id, S>, StateDescriptor<S>]
+ | [
+ StoreWithState<Id, S>,
+ StateDescriptor<S>,
+ InjectionKey<Store<Id, S, G, A>>
+ ]
| undefined
if (!storeAndDescriptor) {
storeAndDescriptor = initStore(id, state, pinia.state.value[id])
options
)
+ // allow children to reuse this store instance to avoid creating a new
+ // store for each child
+ if (shouldProvide) {
+ provide(storeAndDescriptor[2], store)
+ }
+
if (
IS_CLIENT &&
__BROWSER__ &&
return store
}
- return buildStoreToUse(
- storeAndDescriptor[0],
- storeAndDescriptor[1],
- id,
- getters as Record<string, Method> | undefined,
- actions as Record<string, Method> | undefined,
- // @ts-ignore: because we don't have extend on G and A
- options
+ return (
+ // null avoids the warning for not found injection key
+ (hasInstance && inject(storeAndDescriptor[2], null)) ||
+ buildStoreToUse(
+ storeAndDescriptor[0],
+ storeAndDescriptor[1],
+ id,
+ getters as Record<string, Method> | undefined,
+ actions as Record<string, Method> | undefined,
+ // @ts-ignore: because we don't have extend on G and A
+ options
+ )
)
}