]> git.ipfire.org Git - thirdparty/vuejs/pinia.git/commitdiff
refactor: cache store create per instance
authorEduardo San Martin Morote <posva13@gmail.com>
Wed, 7 Apr 2021 14:31:56 +0000 (16:31 +0200)
committerEduardo San Martin Morote <posva@users.noreply.github.com>
Thu, 8 Apr 2021 12:55:28 +0000 (14:55 +0200)
src/mapHelpers.ts
src/plugin.ts
src/rootStore.ts
src/types.ts

index b8ae0a97e178b4aad353cb21722584338a9ce672..42f2b883ca7fccffb7153866c81e283eb143a92c 100644 (file)
@@ -1,4 +1,6 @@
+import type Vue from 'vue'
 import {
+  GenericStore,
   GenericStoreDefinition,
   Method,
   StateTree,
@@ -21,8 +23,21 @@ type Spread<A extends readonly any[]> = A extends [infer L, ...infer R]
   ? StoreObject<L> & Spread<R>
   : unknown
 
+function getCachedStore<
+  Id extends string = string,
+  S extends StateTree = StateTree,
+  G = Record<string, Method>,
+  A = Record<string, Method>
+>(vm: Vue, useStore: StoreDefinition<Id, S, G, A>): Store<Id, S, G, A> {
+  const cache = vm._pStores || (vm._pStores = {})
+  const id = useStore.$id
+  return (cache[id] || (cache[id] = useStore(vm.$pinia))) as Store<Id, S, G, A>
+}
+
 /**
- * Allows using stores without the composition API (`setup()`) by generating an object to be spread in the `computed` field of a component.
+ * Allows using stores without the composition API (`setup()`) by generating an
+ * object to be spread in the `computed` field of a component. it accepts a list
+ * of store definitions.
  *
  * @example
  * ```js
@@ -31,7 +46,7 @@ type Spread<A extends readonly any[]> = A extends [infer L, ...infer R]
  *     // other computed properties
  *     ...mapStores(useUserStore, useCartStore)
  *   },
-
+ *
  *   created() {
  *     this.userStore // store with id "user"
  *     this.cartStore // store with id "cart"
@@ -41,13 +56,13 @@ type Spread<A extends readonly any[]> = A extends [infer L, ...infer R]
  *
  * @param stores - list of stores to map to an object
  */
-export function mapStores<Stores extends unknown[]>(
+export function mapStores<Stores extends GenericStoreDefinition[]>(
   ...stores: [...Stores]
 ): Spread<Stores> {
   return stores.reduce((reduced, useStore) => {
     // @ts-ignore: $id is added by defineStore
-    reduced[useStore.$id + 'Store'] = function () {
-      return (useStore as GenericStoreDefinition)((this as any).$pinia)
+    reduced[useStore.$id + 'Store'] = function (this: Vue) {
+      return getCachedStore(this, useStore)
     }
     return reduced
   }, {} as Spread<Stores>)
@@ -91,15 +106,15 @@ export function mapState<
 ): MapStateReturn<S, G> | MapStateObjectReturn<S, G, KeyMapper> {
   return Array.isArray(keysOrMapper)
     ? keysOrMapper.reduce((reduced, key) => {
-        // @ts-ignore: too complicated for TS
-        reduced[key] = function () {
-          return useStore((this as any).$pinia)[key]
+        // @ts-ignore: sorry TS
+        reduced[key] = function (this: Vue) {
+          return getCachedStore(this, useStore)[key]
         }
         return reduced
       }, {} as MapStateReturn<S, G>)
     : Object.keys(keysOrMapper).reduce((reduced, key: keyof KeyMapper) => {
-        reduced[key] = function () {
-          return useStore((this as any).$pinia)[keysOrMapper[key]]
+        reduced[key] = function (this: Vue) {
+          return getCachedStore(this, useStore)[keysOrMapper[key]]
         }
         return reduced
       }, {} as MapStateObjectReturn<S, G, KeyMapper>)
index 4f62bc9fb1f945bc124a04b15e0750db713ca174..a95e70962cc1893b39524537d369fce065337adf 100644 (file)
@@ -51,5 +51,9 @@ export const PiniaPlugin: PluginFunction<void> = function (_Vue) {
         this.$pinia = options.parent.$pinia
       }
     },
+    destroyed() {
+      // @ts-ignore: clear up the store cache
+      delete this._pStores
+    },
   })
 }
index 850edbcbc5b0a42e4bf5b894e900af2008535958..67b318f4ad72e053c69283011ef5e8f61cd4378e 100644 (file)
@@ -4,6 +4,7 @@ import {
   StoreWithState,
   StateDescriptor,
   PiniaCustomProperties,
+  GenericStore,
 } from './types'
 import { VueConstructor } from 'vue'
 import type Vue from 'vue'
@@ -65,12 +66,25 @@ export interface Pinia {
 
 declare module 'vue/types/vue' {
   interface Vue {
+    /**
+     * Currently installed pinia instance.
+     */
     $pinia: Pinia
+
+    /**
+     * Cache of stores instantiated by the current instance. Used by map
+     * helpers.
+     */
+    _pStores?: Record<string, GenericStore>
   }
 }
 
 declare module 'vue/types/options' {
   interface ComponentOptions<V extends Vue> {
+    /**
+     * Pinia instance to install in your application. Should be passed to the
+     * root Vue.
+     */
     pinia?: Pinia
   }
 }
@@ -91,7 +105,7 @@ export function createPinia(): Pinia {
 
     use(plugin) {
       /* istanbul ignore next */
-      if (__DEV__) {
+      if (__DEV__ && !__TEST__) {
         console.warn(
           `[🍍]: The plugin API has plans to change to bring better extensibility. "pinia.use()" signature will change in the next release. It is recommended to avoid using this API.`
         )
index 736fd9458cb7cc83295c02e9549eed9e2cdea376..9ab1fcc73ecc34ceec6c46144a8b58e3d0ccd3be 100644 (file)
@@ -112,11 +112,11 @@ export type StoreWithGetters<G> = {
 //     : never
 // }
 
-// has the actions without the context (this) for typings
 export type Store<
   Id extends string,
   S extends StateTree,
   G,
+  // has the actions without the context (this) for typings
   A
 > = StoreWithState<Id, S> &
   S &
@@ -138,7 +138,7 @@ export interface StoreDefinition<
 }
 
 /**
- * Generic version of Store
+ * Generic version of Store.
  */
 export type GenericStore = Store<
   string,
@@ -148,17 +148,14 @@ export type GenericStore = Store<
 >
 
 /**
- * Generic version of `StoreDefinition`
+ * Generic version of `StoreDefinition`.
  */
-export interface GenericStoreDefinition {
-  (pinia?: Pinia | null | undefined): Store<
-    string,
-    StateTree,
-    Record<string, Method>,
-    Record<string, Method>
-  >
-  $id: string
-}
+export type GenericStoreDefinition = StoreDefinition<
+  string,
+  StateTree,
+  Record<string, Method>,
+  Record<string, Method>
+>
 
 export interface DevtoolHook {
   on(