Skip to content

Commit

Permalink
feat: reuse store instances when possible
Browse files Browse the repository at this point in the history
  • Loading branch information
posva committed Apr 29, 2021
1 parent 5a6fda6 commit 14f5a5f
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 13 deletions.
30 changes: 29 additions & 1 deletion __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
Expand Down Expand Up @@ -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: `<child/>`,
},
{ global: { plugins: [pinia] } }
)

expect(s1).toBeDefined()
expect(s1).toBe(s2)
})
})
9 changes: 8 additions & 1 deletion src/rootStore.ts
Expand Up @@ -49,7 +49,14 @@ export const getActivePinia = () => {

export const storesMap = new WeakMap<
Pinia,
Map<string, [StoreWithState<string, StateTree>, StateDescriptor<StateTree>]>
Map<
string,
[
StoreWithState<string, StateTree>,
StateDescriptor<StateTree>,
InjectionKey<GenericStore>
]
>
>()

/**
Expand Down
54 changes: 43 additions & 11 deletions src/store.ts
Expand Up @@ -6,6 +6,8 @@ import {
getCurrentInstance,
reactive,
onUnmounted,
InjectionKey,
provide,
} from 'vue'
import {
StateTree,
Expand All @@ -20,6 +22,7 @@ import {
Method,
DefineStoreOptions,
StoreDefinition,
GenericStore,
} from './types'
import {
getActivePinia,
Expand Down Expand Up @@ -90,7 +93,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.state.value[$id] = initialState || buildState()
// const state: Ref<S> = toRef(_p.state.value, $id)
Expand Down Expand Up @@ -172,6 +179,13 @@ function initStore<Id extends string, S extends StateTree>(
$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,
{
Expand All @@ -182,6 +196,7 @@ function initStore<Id extends string, S extends StateTree>(
isListening = true
},
},
injectionSymbol,
]
}

Expand Down Expand Up @@ -271,16 +286,23 @@ export function defineStore<
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()
let stores = storesMap.get(pinia)
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])
Expand All @@ -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__ &&
Expand All @@ -321,14 +349,18 @@ export function defineStore<
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
)
)
}

Expand Down

0 comments on commit 14f5a5f

Please sign in to comment.