]> git.ipfire.org Git - thirdparty/vuejs/pinia.git/commitdiff
refactor: defineStore with setup
authorEduardo San Martin Morote <posva13@gmail.com>
Fri, 9 Jul 2021 14:16:51 +0000 (16:16 +0200)
committerEduardo San Martin Morote <posva13@gmail.com>
Mon, 19 Jul 2021 09:51:12 +0000 (11:51 +0200)
__tests__/actions.spec.ts
__tests__/getters.spec.ts
__tests__/onAction.spec.ts
__tests__/state.spec.ts
__tests__/store.patch.spec.ts
__tests__/store.spec.ts
__tests__/storeSetup.spec.ts [new file with mode: 0644]
src/store.ts

index 746dc0116446ef5f935104dfd4b232d597ac68a9..70f74f02fae286ed0ec6a8b0573c82b73deaa7f1 100644 (file)
@@ -77,7 +77,9 @@ describe('Actions', () => {
   it('store is forced as the context', () => {
     const store = useStore()
     expect(store.$state.a).toBe(true)
-    store.toggle.call(null)
+    expect(() => {
+      store.toggle.call(null)
+    }).not.toThrow()
     expect(store.$state.a).toBe(false)
   })
 
index 270d756cf4d20863d7f5d166dd8f480934246f16..50fbd8bdad18e63b64ba308865bcd76b054f4ee4 100644 (file)
@@ -28,7 +28,7 @@ describe('Getters', () => {
       actions: {
         o() {
           // @ts-expect-error it should type getters
-          this.arrowUper.toUpperCase()
+          this.arrowUpper.toUpperCase()
           this.o().toUpperCase()
           return 'a string'
         },
index 2789a9ec691ce239887faccde4c5d428b74ab3e7..a90a09d013cd85e38aeebf5890945bff3d9c2ae0 100644 (file)
@@ -147,7 +147,7 @@ describe('Subscriptions', () => {
       const s1 = useStore()
       const s2 = useStore()
 
-      expect(s2).not.toBe(s1)
+      expect(s2).toBe(s1)
 
       const spy1 = jest.fn()
       const spy2 = jest.fn()
index f47836f1bf380240c639cd9ca44c15140108877f..a69460a48e9625ecd4255803dc346b08802375f8 100644 (file)
@@ -77,8 +77,8 @@ describe('State', () => {
     expect(store.$state.name).toBe('Eduardo')
     expect(pinia.state.value.main).toEqual({
       name: 'Eduardo',
-      counter: 0,
       double: 0,
+      counter: 0,
     })
 
     name.value = 'Ed'
index 9156f5439ed75dceb679198edc9b65cb92c9a00d..8e6c157987d6637ee2f88b95042f8a239d06c51a 100644 (file)
@@ -1,5 +1,10 @@
 import { reactive, ref } from 'vue'
-import { createPinia, defineStore, setActivePinia } from '../src'
+import {
+  createPinia,
+  defineSetupStore,
+  defineStore,
+  setActivePinia,
+} from '../src'
 
 describe('store.$patch', () => {
   const useStore = () => {
index 667aa0921cba0c80d107fd1b67dd350e33fdfd4e..0b09007217403d58954d29e89cab61b44b89ab0a 100644 (file)
@@ -20,6 +20,12 @@ describe('Store', () => {
     })()
   }
 
+  it('reuses a store', () => {
+    setActivePinia(createPinia())
+    const useStore = defineStore({ id: 'main' })
+    expect(useStore()).toBe(useStore())
+  })
+
   it('sets the initial state', () => {
     const store = useStore()
     expect(store.$state).toEqual({
diff --git a/__tests__/storeSetup.spec.ts b/__tests__/storeSetup.spec.ts
new file mode 100644 (file)
index 0000000..6431f9c
--- /dev/null
@@ -0,0 +1,122 @@
+import {
+  createPinia,
+  defineSetupStore,
+  defineStore,
+  setActivePinia,
+} from '../src'
+import { computed, nextTick, ref, watch } from 'vue'
+
+function expectType<T>(value: T): void {}
+
+describe('store with setup syntax', () => {
+  const useStore = defineSetupStore('main', () => {
+    const name = ref('Eduardo')
+    const counter = ref(0)
+    function increment(amount = 1) {
+      counter.value += amount
+    }
+    const double = computed(() => counter.value * 2)
+
+    return { name, counter, increment, double }
+  })
+
+  beforeEach(() => {
+    setActivePinia(createPinia())
+  })
+
+  it('should extract the $state', () => {
+    const store = useStore()
+    expectType<{ name: string; counter: number }>(store.$state)
+    expect(store.$state).toEqual({ name: 'Eduardo', counter: 0 })
+    expect(store.name).toBe('Eduardo')
+    expect(store.counter).toBe(0)
+    expect(store.double).toBe(0)
+    store.increment()
+    expect(store.counter).toBe(1)
+    expect(store.double).toBe(2)
+    expect(store.$state).toEqual({ name: 'Eduardo', counter: 1 })
+    expect(store.$state).not.toHaveProperty('double')
+    expect(store.$state).not.toHaveProperty('increment')
+  })
+
+  it('can directly access state at the store level', () => {
+    const store = useStore()
+
+    expect(store.name).toBe('Eduardo')
+    store.name = 'Ed'
+    expect(store.name).toBe('Ed')
+  })
+
+  it('state is reactive', () => {
+    const store = useStore()
+    const upperCased = computed(() => store.name.toUpperCase())
+    expect(upperCased.value).toBe('EDUARDO')
+    store.name = 'Ed'
+    expect(upperCased.value).toBe('ED')
+  })
+
+  // it('watch', () => {
+  //   setActivePinia(createPinia())
+  //   defineStore({
+  //     id: 'main',
+  //     state: () => ({
+  //       name: 'Eduardo',
+  //       counter: 0,
+  //     }),
+  //   })()
+  // })
+
+  it('state can be watched', async () => {
+    const store = useStore()
+    const spy = jest.fn()
+    watch(() => store.name, spy)
+    expect(spy).not.toHaveBeenCalled()
+    store.name = 'Ed'
+    await nextTick()
+    expect(spy).toHaveBeenCalledTimes(1)
+  })
+
+  it('unwraps refs', () => {
+    const name = ref('Eduardo')
+    const counter = ref(0)
+    const double = computed({
+      get: () => counter.value * 2,
+      set(val) {
+        counter.value = val / 2
+      },
+    })
+
+    const pinia = createPinia()
+    setActivePinia(pinia)
+    const useStore = defineStore({
+      id: 'main',
+      state: () => ({
+        name,
+        counter,
+        double,
+      }),
+    })
+
+    const store = useStore()
+
+    expect(store.name).toBe('Eduardo')
+    expect(store.$state.name).toBe('Eduardo')
+    expect(pinia.state.value.main).toEqual({
+      name: 'Eduardo',
+      counter: 0,
+      double: 0,
+    })
+
+    name.value = 'Ed'
+    expect(store.name).toBe('Ed')
+    expect(store.$state.name).toBe('Ed')
+    expect(pinia.state.value.main.name).toBe('Ed')
+
+    store.name = 'Edu'
+    expect(store.name).toBe('Edu')
+
+    store.$patch({ counter: 2 })
+    expect(store.counter).toBe(2)
+    expect(counter.value).toBe(2)
+  })
+})
index 95d0c70726d01986ab19d68c79f549f1cde4297a..72a95f9af3f4d3de722714d7363188d9ee2c1b3b 100644 (file)
@@ -5,8 +5,6 @@ import {
   inject,
   getCurrentInstance,
   reactive,
-  InjectionKey,
-  provide,
   DebuggerEvent,
   WatchOptions,
   UnwrapRef,
@@ -18,24 +16,20 @@ import {
   onUnmounted,
   ComputedRef,
   toRef,
+  toRefs,
 } from 'vue'
 import {
   StateTree,
-  StoreWithState,
   SubscriptionCallback,
   DeepPartial,
   isPlainObject,
-  StoreWithGetters,
   Store,
-  StoreWithActions,
   _Method,
-  StateDescriptor,
   DefineStoreOptions,
   StoreDefinition,
   GettersTree,
   MutationType,
   StoreOnActionListener,
-  UnwrapPromise,
   ActionsTree,
   SubscriptionCallbackMutation,
   _UnionToTuple,
@@ -43,7 +37,6 @@ import {
 import {
   getActivePinia,
   setActivePinia,
-  storesMap,
   piniaSymbol,
   Pinia,
   activePinia,
@@ -102,314 +95,62 @@ function computedFromState<T, Id extends string>(
   return reactiveObject
 }
 
-/**
- * Creates a store with its state object. This is meant to be augmented with getters and actions
- *
- * @param id - unique identifier of the store, like a name. eg: main, cart, user
- * @param buildState - function to build the initial state
- * @param initialState - initial state applied to the store, Must be correctly typed to infer typings
- */
-function initStore<
+export interface DefineSetupStoreOptions<
   Id extends string,
   S extends StateTree,
-  G extends GettersTree<S>,
-  A /* extends ActionsTree */
->(
-  $id: Id,
-  buildState: () => S = () => ({} as S),
-  initialState?: S | undefined
-): [
-  StoreWithState<Id, S, G, A>,
-  { get: () => S; set: (newValue: S) => void },
-  InjectionKey<Store>
-] {
-  const pinia = getActivePinia()
-  pinia.state.value[$id] = initialState || buildState()
-  // const state: Ref<S> = toRef(_p.state.value, $id)
-
-  // internal state
-  let isListening = true
-  let subscriptions: SubscriptionCallback<S>[] = markRaw([])
-  let actionSubscriptions: StoreOnActionListener<Id, S, G, A>[] = markRaw([])
-  let debuggerEvents: DebuggerEvent[] | DebuggerEvent
-
-  function $patch(stateMutation: (state: UnwrapRef<S>) => void): void
-  function $patch(partialState: DeepPartial<UnwrapRef<S>>): void
-  function $patch(
-    partialStateOrMutator:
-      | DeepPartial<UnwrapRef<S>>
-      | ((state: UnwrapRef<S>) => void)
-  ): void {
-    let subscriptionMutation: SubscriptionCallbackMutation<S>
-    isListening = false
-    // reset the debugger events since patches are sync
-    /* istanbul ignore else */
-    if (__DEV__) {
-      debuggerEvents = []
-    }
-    if (typeof partialStateOrMutator === 'function') {
-      partialStateOrMutator(pinia.state.value[$id] as UnwrapRef<S>)
-      subscriptionMutation = {
-        type: MutationType.patchFunction,
-        storeId: $id,
-        events: debuggerEvents as DebuggerEvent[],
-      }
-    } else {
-      innerPatch(pinia.state.value[$id], partialStateOrMutator)
-      subscriptionMutation = {
-        type: MutationType.patchObject,
-        payload: partialStateOrMutator,
-        storeId: $id,
-        events: debuggerEvents as DebuggerEvent[],
-      }
-    }
-    isListening = true
-    // because we paused the watcher, we need to manually call the subscriptions
-    subscriptions.forEach((callback) => {
-      callback(subscriptionMutation, pinia.state.value[$id] as UnwrapRef<S>)
-    })
-  }
-
-  function $subscribe(callback: SubscriptionCallback<S>) {
-    subscriptions.push(callback)
-
-    // watch here to link the subscription to the current active instance
-    // e.g. inside the setup of a component
-    const options: WatchOptions = { deep: true, flush: 'sync' }
-    /* istanbul ignore else */
-    if (__DEV__) {
-      options.onTrigger = (event) => {
-        if (isListening) {
-          debuggerEvents = event
-        } else {
-          // let patch send all the events together later
-          /* istanbul ignore else */
-          if (Array.isArray(debuggerEvents)) {
-            debuggerEvents.push(event)
-          } else {
-            console.error(
-              '🍍 debuggerEvents should be an array. This is most likely an internal Pinia bug.'
-            )
-          }
-        }
-      }
-    }
-    const stopWatcher = watch(
-      () => pinia.state.value[$id] as UnwrapRef<S>,
-      (state, oldState) => {
-        if (isListening) {
-          callback(
-            {
-              storeId: $id,
-              type: MutationType.direct,
-              events: debuggerEvents as DebuggerEvent,
-            },
-            state
-          )
-        }
-      },
-      options
-    )
-
-    const removeSubscription = () => {
-      const idx = subscriptions.indexOf(callback)
-      if (idx > -1) {
-        subscriptions.splice(idx, 1)
-        stopWatcher()
-      }
-    }
-
-    if (getCurrentInstance()) {
-      onUnmounted(removeSubscription)
-    }
-
-    return removeSubscription
-  }
-
-  function $onAction(callback: StoreOnActionListener<Id, S, G, A>) {
-    actionSubscriptions.push(callback)
-
-    const removeSubscription = () => {
-      const idx = actionSubscriptions.indexOf(callback)
-      if (idx > -1) {
-        actionSubscriptions.splice(idx, 1)
-      }
-    }
-
-    if (getCurrentInstance()) {
-      onUnmounted(removeSubscription)
-    }
-
-    return removeSubscription
-  }
-
-  function $reset() {
-    pinia.state.value[$id] = buildState()
-  }
-
-  const storeWithState: StoreWithState<Id, S, G, A> = {
-    $id,
-    _p: pinia,
-    _as: actionSubscriptions as unknown as StoreOnActionListener[],
-
-    // $state is added underneath
+  G extends ActionsTree, // TODO: naming
+  A extends ActionsTree
+> {
+  hydrate?(store: Store<Id, S, G, A>, initialState: S | undefined): void
+}
 
-    $patch,
-    $subscribe,
-    $onAction,
-    $reset,
-  } as StoreWithState<Id, S, G, A>
-
-  const injectionSymbol = __DEV__
-    ? Symbol(`PiniaStore(${$id})`)
-    : /* istanbul ignore next */
-      Symbol()
-
-  return [
-    storeWithState,
-    {
-      get: () => pinia.state.value[$id] as S,
-      set: (newState: S) => {
-        isListening = false
-        pinia.state.value[$id] = newState
-        isListening = true
-      },
-    },
-    injectionSymbol,
-  ]
+function isComputed(o: any): o is ComputedRef {
+  return o && o.effect && o.effect.computed
 }
 
-const noop = () => {}
-/**
- * Creates a store bound to the lifespan of where the function is called. This
- * means creating the store inside of a component's setup will bound it to the
- * lifespan of that component while creating it outside of a component will
- * create an ever living store
- *
- * @param partialStore - store with state returned by initStore
- * @param descriptor - descriptor to setup $state property
- * @param $id - unique name of the store
- * @param getters - getters of the store
- * @param actions - actions of the store
- */
-function buildStoreToUse<
+function createOptionsStore<
   Id extends string,
   S extends StateTree,
   G extends GettersTree<S>,
   A extends ActionsTree
->(
-  partialStore: StoreWithState<Id, S, G, A>,
-  descriptor: StateDescriptor<S>,
-  $id: Id,
-  getters: G = {} as G,
-  actions: A = {} as A,
-  options: DefineStoreOptions<Id, S, G, A>
-) {
-  const pinia = getActivePinia()
-
-  const computedGetters: StoreWithGetters<G> = {} as StoreWithGetters<G>
-  for (const getterName in getters) {
-    // @ts-ignore: it's only readonly for the users
-    computedGetters[getterName] = computed(() => {
-      setActivePinia(pinia)
-      // eslint-disable-next-line @typescript-eslint/no-use-before-define
-      // @ts-expect-error: the argument count is correct
-      return getters[getterName].call(store, store)
-    }) as StoreWithGetters<G>[typeof getterName]
+>(options: DefineStoreOptions<Id, S, G, A>, pinia: Pinia): Store<Id, S, G, A> {
+  const { id, state, actions, getters } = options
+  function $reset() {
+    pinia.state.value[id] = state ? state() : {}
   }
 
-  const wrappedActions: StoreWithActions<A> = {} as StoreWithActions<A>
-  for (const actionName in actions) {
-    wrappedActions[actionName] = function (this: Store<Id, S, G, A>) {
-      setActivePinia(pinia)
-      const args = Array.from(arguments) as Parameters<A[typeof actionName]>
-      const localStore = this || store
-
-      let afterCallback: (
-        resolvedReturn: UnwrapPromise<ReturnType<A[typeof actionName]>>
-      ) => void = noop
-      let onErrorCallback: (error: unknown) => void = noop
-      function after(callback: typeof afterCallback) {
-        afterCallback = callback
-      }
-      function onError(callback: typeof onErrorCallback) {
-        onErrorCallback = callback
-      }
-
-      partialStore._as.forEach((callback) => {
-        // @ts-expect-error
-        callback({ args, name: actionName, store: localStore, after, onError })
-      })
-
-      let ret: ReturnType<A[typeof actionName]>
-      try {
-        ret = actions[actionName].apply(localStore, args as unknown as any[])
-        Promise.resolve(ret).then(afterCallback).catch(onErrorCallback)
-      } catch (error) {
-        onErrorCallback(error)
-        throw error
-      }
+  function setup() {
+    $reset()
+    // pinia.state.value[id] = state ? state() : {}
 
-      return ret
-    } as StoreWithActions<A>[typeof actionName]
-  }
-
-  const store: Store<Id, S, G, A> = reactive(
-    assign(
-      __DEV__ && IS_CLIENT
-        ? // devtools custom properties
-          {
-            _customProperties: markRaw(new Set<string>()),
-          }
-        : {},
-      partialStore,
-      // using this means no new properties can be added as state
-      computedFromState(pinia.state, $id),
-      computedGetters,
-      wrappedActions
+    return assign(
+      toRefs(pinia.state.value[id]),
+      actions,
+      Object.keys(getters || {}).reduce((computedGetters, name) => {
+        computedGetters[name] = computed(() => {
+          setActivePinia(pinia)
+          // @ts-expect-error
+          return getters![name].call(store, store)
+        })
+        return computedGetters
+      }, {} as Record<string, ComputedRef>)
     )
-  ) as Store<Id, S, G, A>
-
-  // use this instead of a computed with setter to be able to create it anywhere
-  // without linking the computed lifespan to wherever the store is first
-  // created.
-  Object.defineProperty(store, '$state', descriptor)
-
-  // add getters for devtools
-  if (__DEV__ && IS_CLIENT) {
-    store._getters = markRaw(Object.keys(getters))
   }
 
-  // apply all plugins
-  pinia._p.forEach((extender) => {
-    if (__DEV__ && IS_CLIENT) {
-      // @ts-expect-error: conflict between A and ActionsTree
-      const extensions = extender({ store, app: pinia._a, pinia, options })
-      Object.keys(extensions || {}).forEach((key) =>
-        store._customProperties.add(key)
-      )
-      assign(store, extensions)
-    } else {
-      // @ts-expect-error: conflict between A and ActionsTree
-      assign(store, extender({ store, app: pinia._a, pinia, options }))
-    }
-  })
+  const store = createSetupStore(
+    id,
+    setup,
+    // TODO: actual hydrate option to be added to options store
+    // @ts-expect-error: fixme
+    options
+  )
 
-  return store
-}
+  store.$reset = $reset
 
-export interface DefineSetupStoreOptions<
-  Id extends string,
-  S extends StateTree,
-  G extends ActionsTree, // TODO: naming
-  A extends ActionsTree
-> {
-  hydrate?(store: Store<Id, S, G, A>, initialState: S | undefined): void
+  return store as any
 }
 
-function isComputed(o: any): o is ComputedRef {
-  return o && o.effect && o.effect.computed
-}
+const noop = () => {}
 
 function createSetupStore<
   Id extends string,
@@ -420,13 +161,13 @@ function createSetupStore<
 >(
   $id: Id,
   setup: () => SS,
-  {
-    // @ts-expect-error
-    hydrate = innerPatch,
-  }: DefineSetupStoreOptions<Id, S, G, A> = {}
+  options: DefineSetupStoreOptions<Id, S, G, A> = {}
 ): Store<Id, S, G, A> {
   const pinia = getActivePinia()
   let scope!: EffectScope
+  const hydrate = options.hydrate || innerPatch
+  // @ts-expect-error
+  const state = options.state
 
   // watcher options for $subscribe
   const $subscribeOptions: WatchOptions = { deep: true, flush: 'sync' }
@@ -585,8 +326,14 @@ function createSetupStore<
   for (const key in setupStore) {
     const prop = setupStore[key]
 
-    // action
-    if (typeof prop === 'function') {
+    if ((isRef(prop) && !isComputed(prop)) || isReactive(prop)) {
+      // @ts-expect-error: fixme
+      if (!options.state) {
+        // mark it as a piece of state to be serialized
+        pinia.state.value[$id][key] = toRef(setupStore as any, key)
+      }
+      // action
+    } else if (typeof prop === 'function') {
       // @ts-expect-error: we are overriding the function
       setupStore[key] = function () {
         setActivePinia(pinia)
@@ -614,7 +361,7 @@ function createSetupStore<
 
         let ret: any
         try {
-          ret = prop.apply(this, args)
+          ret = prop.apply(this || store, args)
           Promise.resolve(ret).then(afterCallback).catch(onErrorCallback)
         } catch (error) {
           onErrorCallback(error)
@@ -623,9 +370,6 @@ function createSetupStore<
 
         return ret
       }
-    } else if ((isRef(prop) && !isComputed(prop)) || isReactive(prop)) {
-      // mark it as a piece of state to be serialized
-      pinia.state.value[$id][key] = toRef(setupStore as any, key)
     } else if (__DEV__ && IS_CLIENT) {
       // add getters for devtools
       if (isComputed(prop)) {
@@ -695,11 +439,10 @@ function createSetupStore<
   return store
 }
 
-// const useStore = createSetupStore('cosa', () => {
-//   return {
-//     o: 'one',
-//   }
-// })
+// export function disposeStore(store: Store) {
+//   store._e
+
+// }
 
 type _SpreadStateFromStore<SS, K extends readonly any[]> = K extends readonly [
   infer A,
@@ -833,13 +576,10 @@ export function defineStore<
   // cannot extends ActionsTree because we loose the typings
   A /* extends ActionsTree */
 >(options: DefineStoreOptions<Id, S, G, A>): StoreDefinition<Id, S, G, A> {
-  const { id, state, getters, actions } = options
+  const { id } = options
 
-  function useStore(pinia?: Pinia | null): Store<Id, S, G, A> {
+  function useStore(pinia?: Pinia | null) {
     const currentInstance = getCurrentInstance()
-    // only run provide when pinia hasn't been manually passed
-    const shouldProvide = currentInstance && !pinia
-    // avoid injecting if `useStore` when not possible
     pinia =
       // in test mode, ignore the argument provided as we can always retrieve a
       // pinia instance with getActivePinia()
@@ -848,76 +588,31 @@ export function defineStore<
     if (pinia) setActivePinia(pinia)
     // TODO: worth warning on server if no piniaKey as it can leak data
     pinia = getActivePinia()
-    let storeCache = storesMap.get(pinia)
-    if (!storeCache) storesMap.set(pinia, (storeCache = new Map()))
-
-    let storeAndDescriptor = storeCache.get(id) as
-      | [
-          StoreWithState<Id, S, G, A>,
-          StateDescriptor<S>,
-          InjectionKey<Store<Id, S, G, A>>
-        ]
-      | undefined
-
-    let store: Store<Id, S, G, A>
-
-    if (!storeAndDescriptor) {
-      storeAndDescriptor = initStore(id, state, pinia.state.value[id])
-
-      // @ts-expect-error: annoying to type
-      storeCache.set(id, storeAndDescriptor)
-
-      store = buildStoreToUse<
-        Id,
-        S,
-        G,
-        // @ts-expect-error: A without extends
-        A
-      >(
-        storeAndDescriptor[0],
-        storeAndDescriptor[1],
-        id,
-        getters,
-        actions,
-        options
-      )
 
-      // allow children to reuse this store instance to avoid creating a new
-      // store for each child
-      if (shouldProvide) {
-        provide(storeAndDescriptor[2], store)
-      }
-    } else {
-      store =
-        (currentInstance && inject(storeAndDescriptor[2], null)) ||
-        buildStoreToUse<
-          Id,
-          S,
-          G,
-          // @ts-expect-error: cannot extends ActionsTree
-          A
-        >(
-          storeAndDescriptor[0],
-          storeAndDescriptor[1],
-          id,
-          getters,
-          actions,
-          options
+    if (!pinia._s.has(id)) {
+      pinia._s.set(
+        id,
+        createOptionsStore(
+          // @ts-expect-error: bad actions
+          options,
+          pinia
         )
+      )
     }
 
+    const store: Store<Id, S, G, A> = pinia._s.get(id)! as Store<Id, S, G, A>
+
     // save stores in instances to access them devtools
     if (__DEV__ && IS_CLIENT && currentInstance && currentInstance.proxy) {
       const vm = currentInstance.proxy
       const cache = '_pStores' in vm ? vm._pStores! : (vm._pStores = {})
       // @ts-expect-error: still can't cast Store with generics to Store
-      cache[store.$id] = store
+      cache[id] = store
     }
 
     return store
   }
 
-  // needed by map helpers
   useStore.$id = id
 
   return useStore