From: Eduardo San Martin Morote Date: Thu, 29 Apr 2021 12:37:09 +0000 (+0200) Subject: feat: reuse store instances when possible X-Git-Tag: v2.0.0-alpha.14~25 X-Git-Url: http://git.ipfire.org/gitweb/gitweb.cgi?a=commitdiff_plain;h=14f5a5fd21677e7e5673443c35eeadbe2bdd8f05;p=thirdparty%2Fvuejs%2Fpinia.git feat: reuse store instances when possible --- diff --git a/__tests__/store.spec.ts b/__tests__/store.spec.ts index c94bcc13..6699b0ee 100644 --- a/__tests__/store.spec.ts +++ b/__tests__/store.spec.ts @@ -1,6 +1,6 @@ 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 @@ -246,4 +246,32 @@ describe('Store', () => { 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: ``, + }, + { global: { plugins: [pinia] } } + ) + + expect(s1).toBeDefined() + expect(s1).toBe(s2) + }) }) diff --git a/src/rootStore.ts b/src/rootStore.ts index 03c5761c..8c115f70 100644 --- a/src/rootStore.ts +++ b/src/rootStore.ts @@ -49,7 +49,14 @@ export const getActivePinia = () => { export const storesMap = new WeakMap< Pinia, - Map, StateDescriptor]> + Map< + string, + [ + StoreWithState, + StateDescriptor, + InjectionKey + ] + > >() /** diff --git a/src/store.ts b/src/store.ts index fc3dada9..cfe8a3c7 100644 --- a/src/store.ts +++ b/src/store.ts @@ -6,6 +6,8 @@ import { getCurrentInstance, reactive, onUnmounted, + InjectionKey, + provide, } from 'vue' import { StateTree, @@ -20,6 +22,7 @@ import { Method, DefineStoreOptions, StoreDefinition, + GenericStore, } from './types' import { getActivePinia, @@ -90,7 +93,11 @@ function initStore( $id: Id, buildState: () => S = () => ({} as S), initialState?: S | undefined -): [StoreWithState, { get: () => S; set: (newValue: S) => void }] { +): [ + StoreWithState, + { get: () => S; set: (newValue: S) => void }, + InjectionKey +] { const pinia = getActivePinia() pinia.state.value[$id] = initialState || buildState() // const state: Ref = toRef(_p.state.value, $id) @@ -172,6 +179,13 @@ function initStore( $reset, } as StoreWithState + const injectionSymbol = __DEV__ ? Symbol(`PiniaStore(${$id})`) : Symbol() + + // avoid warnings with injections not found + if (pinia._a) { + pinia._a.provide(injectionSymbol, null) + } + return [ storeWithState, { @@ -182,6 +196,7 @@ function initStore( isListening = true }, }, + injectionSymbol, ] } @@ -271,8 +286,11 @@ export function defineStore< const { id, state, getters, actions } = options function useStore(pinia?: Pinia | null): Store { + 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() @@ -280,7 +298,11 @@ export function defineStore< if (!stores) storesMap.set(pinia, (stores = new Map())) let storeAndDescriptor = stores.get(id) as - | [StoreWithState, StateDescriptor] + | [ + StoreWithState, + StateDescriptor, + InjectionKey> + ] | undefined if (!storeAndDescriptor) { storeAndDescriptor = initStore(id, state, pinia.state.value[id]) @@ -297,6 +319,12 @@ export function defineStore< 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__ && @@ -321,14 +349,18 @@ export function defineStore< return store } - return buildStoreToUse( - storeAndDescriptor[0], - storeAndDescriptor[1], - id, - getters as Record | undefined, - actions as Record | 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 | undefined, + actions as Record | undefined, + // @ts-ignore: because we don't have extend on G and A + options + ) ) }