]> git.ipfire.org Git - thirdparty/vuejs/pinia.git/commitdiff
perf(store): reuse stores from parent to children components
authorEduardo San Martin Morote <posva13@gmail.com>
Mon, 3 May 2021 09:41:33 +0000 (11:41 +0200)
committerEduardo San Martin Morote <posva13@gmail.com>
Mon, 3 May 2021 09:41:33 +0000 (11:41 +0200)
__tests__/store.spec.ts
src/rootStore.ts
src/store.ts

index f0e99b90a24313f38824bd868e5899397c387e4f..322ebdba5aff2a3a87ea2a95fac7a1de94d641a1 100644 (file)
@@ -1,5 +1,13 @@
+import { defineComponent } from '@vue/composition-api'
+import { createLocalVue, mount } from '@vue/test-utils'
 import Vue from 'vue'
-import { createPinia, defineStore, Pinia, setActivePinia } from '../src'
+import {
+  createPinia,
+  defineStore,
+  Pinia,
+  PiniaPlugin,
+  setActivePinia,
+} from '../src'
 
 describe('Store', () => {
   let pinia: Pinia
@@ -154,4 +162,35 @@ describe('Store', () => {
       store.$state
     )
   })
+
+  it('reuses stores from parent components', () => {
+    let s1, s2
+    const useStore = defineStore({ id: 'one' })
+    const pinia = createPinia()
+    pinia.Vue = Vue
+    const localVue = createLocalVue()
+    localVue.use(PiniaPlugin)
+
+    const Child = defineComponent({
+      setup() {
+        s2 = useStore()
+      },
+      template: `<p>child</p>`,
+    })
+
+    mount(
+      defineComponent({
+        setup() {
+          s1 = useStore()
+          return { s1 }
+        },
+        components: { Child },
+        template: `<child/>`,
+      }),
+      { localVue, pinia }
+    )
+
+    expect(s1).toBeDefined()
+    expect(s1).toBe(s2)
+  })
 })
index 8bd294c131047917efe32e81ca7714403b6c4578..11127d889e96f604a3e501c5db56514012781f57 100644 (file)
@@ -21,7 +21,14 @@ import type Vue from 'vue'
 
 export const storesMap = new WeakMap<
   Pinia,
-  Map<string, [StoreWithState<string, StateTree>, StateDescriptor<StateTree>]>
+  Map<
+    string,
+    [
+      StoreWithState<string, StateTree>,
+      StateDescriptor<StateTree>,
+      InjectionKey<GenericStore>
+    ]
+  >
 >()
 
 export const piniaSymbol = (__DEV__
index d991aae530c08fdb493a2e6df669a5eb2858eb72..3c87789688829a74f623ddb714b11407075ab164 100644 (file)
@@ -7,6 +7,8 @@ import {
   markRaw,
   inject,
   onUnmounted,
+  InjectionKey,
+  provide,
 } from '@vue/composition-api'
 import {
   StateTree,
@@ -19,10 +21,10 @@ import {
   StoreWithActions,
   Method,
   StateDescriptor,
-  PiniaCustomProperties,
   StoreDefinition,
   GettersTree,
   DefineStoreOptions,
+  GenericStore,
 } from './types'
 import { useStoreDevtools } from './devtools'
 import {
@@ -92,7 +94,11 @@ function initStore<Id extends string, S extends StateTree>(
   $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.Vue.set(pinia.state.value, $id, initialState || buildState())
   // const state: Ref<S> = toRef(_p.state.value, $id)
@@ -174,6 +180,8 @@ function initStore<Id extends string, S extends StateTree>(
     $reset,
   } as StoreWithState<Id, S>
 
+  const injectionSymbol = __DEV__ ? Symbol(`PiniaStore(${$id})`) : Symbol()
+
   return [
     storeWithState,
     {
@@ -184,6 +192,7 @@ function initStore<Id extends string, S extends StateTree>(
         isListening = true
       },
     },
+    injectionSymbol,
   ]
 }
 
@@ -273,9 +282,11 @@ export function defineStore<
   const { id, state, getters, actions } = options
 
   function useStore(pinia?: Pinia | null): Store<Id, S, G, A> {
-    // const vm = getCurrentInstance()
+    const hasInstance = getCurrentInstance()
+    // only run provide when pinia hasn't been manually passed
+    const shouldProvide = hasInstance && !pinia
     // pinia = pinia || (vm && ((vm as any).$pinia as Pinia))
-    pinia = pinia || (getCurrentInstance() && inject(piniaSymbol))
+    pinia = pinia || (hasInstance && inject(piniaSymbol))
 
     if (pinia) setActivePinia(pinia)
 
@@ -286,7 +297,11 @@ export function defineStore<
 
     // let store = stores.get(id) as Store<Id, S, G, A>
     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) {
@@ -308,17 +323,26 @@ 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)
+      }
+
       return store
     }
 
-    return buildStoreToUse(
-      storeAndDescriptor[0],
-      storeAndDescriptor[1],
-      id,
-      getters as GettersTree<S> | undefined,
-      actions as Record<string, Method> | undefined,
-      // @ts-expect-error: because of the extend on Actions
-      options
+    return (
+      (hasInstance && inject(storeAndDescriptor[2], null)) ||
+      buildStoreToUse(
+        storeAndDescriptor[0],
+        storeAndDescriptor[1],
+        id,
+        getters as GettersTree<S> | undefined,
+        actions as Record<string, Method> | undefined,
+        // @ts-expect-error: because of the extend on Actions
+        options
+      )
     )
   }