]> git.ipfire.org Git - thirdparty/vuejs/pinia.git/commitdiff
refactor(subscribe): better types
authorEduardo San Martin Morote <posva13@gmail.com>
Thu, 13 May 2021 09:37:18 +0000 (11:37 +0200)
committerEduardo San Martin Morote <posva13@gmail.com>
Thu, 13 May 2021 09:37:18 +0000 (11:37 +0200)
__tests__/store.spec.ts
__tests__/subscriptions.spec.ts
src/index.ts
src/store.ts
src/types.ts

index 60f4495641df560265dae4cf89c6c9594c7db97f..35d0068def8d9454f33d1bd30e10d0ec2cf19604 100644 (file)
@@ -1,10 +1,4 @@
-import {
-  createPinia,
-  defineStore,
-  setActivePinia,
-  Pinia,
-  MutationType,
-} from '../src'
+import { createPinia, defineStore, setActivePinia, Pinia } from '../src'
 import { mount } from '@vue/test-utils'
 import { defineComponent, getCurrentInstance, nextTick, watch } from 'vue'
 
@@ -146,41 +140,6 @@ describe('Store', () => {
     expect(store2.$state.nested.a.b).toBe('string')
   })
 
-  it('subscribe to changes', () => {
-    const store = useStore()
-    const spy = jest.fn()
-    store.$subscribe(spy)
-
-    store.$state.a = false
-
-    expect(spy).toHaveBeenCalledWith(
-      expect.objectContaining({
-        payload: {},
-        storeName: 'main',
-        type: MutationType.direct,
-      }),
-      store.$state
-    )
-  })
-
-  it('subscribe to changes done via patch', () => {
-    const store = useStore()
-    const spy = jest.fn()
-    store.$subscribe(spy)
-
-    const patch = { a: false }
-    store.$patch(patch)
-
-    expect(spy).toHaveBeenCalledWith(
-      expect.objectContaining({
-        payload: patch,
-        storeName: 'main',
-        type: MutationType.patchObject,
-      }),
-      store.$state
-    )
-  })
-
   it('should outlive components', async () => {
     const pinia = createPinia()
     const useStore = defineStore({
index 6d9a3c6dd531d7976201ee4f24f14a2d74399543..b07aefc378c75e672be47e8050504f84e93f991f 100644 (file)
@@ -1,4 +1,4 @@
-import { createPinia, defineStore, setActivePinia } from '../src'
+import { createPinia, defineStore, MutationType, setActivePinia } from '../src'
 import { mount } from '@vue/test-utils'
 
 describe('Subscriptions', () => {
@@ -23,6 +23,33 @@ describe('Subscriptions', () => {
     store.$subscribe(spy)
     store.$state.name = 'Cleiton'
     expect(spy).toHaveBeenCalledTimes(1)
+    expect(spy).toHaveBeenCalledWith(
+      expect.objectContaining({
+        storeName: 'main',
+        storeId: 'main',
+        type: MutationType.direct,
+      }),
+      store.$state
+    )
+  })
+
+  it('subscribe to changes done via patch', () => {
+    const store = useStore()
+    const spy = jest.fn()
+    store.$subscribe(spy)
+
+    const patch = { name: 'Cleiton' }
+    store.$patch(patch)
+
+    expect(spy).toHaveBeenCalledWith(
+      expect.objectContaining({
+        payload: patch,
+        storeName: 'main',
+        storeId: 'main',
+        type: MutationType.patchObject,
+      }),
+      store.$state
+    )
   })
 
   it('unsubscribes callback when unsubscribe is called', () => {
index cd8d286bb510cb0994c5effc62fde7f3776443d5..27892b70879dd29a4a35688b7cb62494287c0ce8 100644 (file)
@@ -16,6 +16,7 @@ export type {
   StoreWithState,
   StoreOnActionListener,
   StoreOnActionListenerContext,
+  SubscriptionCallback,
   PiniaCustomProperties,
   DefineStoreOptions,
 } from './types'
index 6b1cc2ccd6ec366562bbf73d2e49c19bcc3029a9..1f459b89e0909e0c02c636d8ff2c52acda85ba95 100644 (file)
@@ -30,6 +30,7 @@ import {
   StoreOnActionListener,
   UnwrapPromise,
   ActionsTree,
+  SubscriptionCallbackMutation,
 } from './types'
 import {
   getActivePinia,
@@ -122,8 +123,7 @@ function initStore<
   function $patch(
     partialStateOrMutator: DeepPartial<S> | ((state: S) => void)
   ): void {
-    let partialState: DeepPartial<S> = {}
-    let type: MutationType
+    let subscriptionMutation: SubscriptionCallbackMutation<S>
     isListening = false
     // reset the debugger events since patches are sync
     /* istanbul ignore else */
@@ -132,19 +132,26 @@ function initStore<
     }
     if (typeof partialStateOrMutator === 'function') {
       partialStateOrMutator(pinia.state.value[$id])
-      type = MutationType.patchFunction
+      subscriptionMutation = {
+        type: MutationType.patchFunction,
+        storeName: $id,
+        storeId: $id,
+        events: debuggerEvents as DebuggerEvent[],
+      }
     } else {
       innerPatch(pinia.state.value[$id], partialStateOrMutator)
-      partialState = partialStateOrMutator
-      type = MutationType.patchObject
+      subscriptionMutation = {
+        type: MutationType.patchObject,
+        payload: partialStateOrMutator,
+        storeName: $id,
+        storeId: $id,
+        events: debuggerEvents as DebuggerEvent[],
+      }
     }
     isListening = true
     // because we paused the watcher, we need to manually call the subscriptions
     subscriptions.forEach((callback) => {
-      callback(
-        { storeName: $id, type, payload: partialState, events: debuggerEvents },
-        pinia.state.value[$id] as UnwrapRef<S>
-      )
+      callback(subscriptionMutation, pinia.state.value[$id] as UnwrapRef<S>)
     })
   }
 
@@ -180,9 +187,9 @@ function initStore<
           callback(
             {
               storeName: $id,
+              storeId: $id,
               type: MutationType.direct,
-              payload: {},
-              events: debuggerEvents,
+              events: debuggerEvents as DebuggerEvent,
             },
             state
           )
index 8aaed3616f059b1e58574caee9d0abcddb689cfa..66c4d6f050bb47c9010b58b948d44677ad4f978e 100644 (file)
@@ -59,8 +59,108 @@ export enum MutationType {
   // maybe reset? for $state = {} and $reset
 }
 
+/**
+ * Base type for the context passed to a subscription callback.
+ *
+ * @internal
+ */
+export interface _SubscriptionCallbackMutationBase {
+  /**
+   * Type of the mutation.
+   */
+  type: MutationType
+
+  /**
+   * @deprecated use `storeId` instead.
+   */
+  storeName: string
+
+  /**
+   * `id` of the store doing the mutation.
+   */
+  storeId: string
+}
+
+/**
+ * Context passed to a subscription callback when directly mutating the state of
+ * a store with `store.someState = newValue` or `store.$state.someState =
+ * newValue`.
+ */
+export interface SubscriptionCallbackMutationDirect
+  extends _SubscriptionCallbackMutationBase {
+  type: MutationType.direct
+
+  /**
+   * DEV ONLY. Different mutation calls.
+   */
+  events: DebuggerEvent
+}
+
+/**
+ * Context passed to a subscription callback when `store.$patch()` is called
+ * with an object.
+ */
+export interface SubscriptionCallbackMutationPatchObject<S>
+  extends _SubscriptionCallbackMutationBase {
+  type: MutationType.patchObject
+
+  /**
+   * DEV ONLY. Array for patch calls.
+   */
+  events: DebuggerEvent[]
+
+  /**
+   * Object passed to `store.$patch()`.
+   */
+  payload: DeepPartial<S>
+}
+
+/**
+ * Context passed to a subscription callback when `store.$patch()` is called
+ * with a function.
+ */
+export interface SubscriptionCallbackMutationPatchFunction
+  extends _SubscriptionCallbackMutationBase {
+  type: MutationType.patchFunction
+
+  /**
+   * DEV ONLY. Array of all the mutations done inside of the callback.
+   */
+  events: DebuggerEvent[]
+
+  /**
+   * Object passed to `store.$patch()`.
+   */
+  // payload: DeepPartial<UnwrapRef<S>>
+}
+
+/**
+ * Context object passed to a subscription callback.
+ */
+export type SubscriptionCallbackMutation<S> =
+  | SubscriptionCallbackMutationDirect
+  | SubscriptionCallbackMutationPatchObject<S>
+  | SubscriptionCallbackMutationPatchFunction
+
 export type UnwrapPromise<T> = T extends Promise<infer V> ? V : T
 
+/**
+ * Callback of a subscription
+ */
+export type SubscriptionCallback<S> = (
+  /**
+   * Object with information relative to the store mutation that triggered the
+   * subscription.
+   */
+  mutation: SubscriptionCallbackMutation<S>,
+
+  /**
+   * State of the store when the subscription is triggered. Same as
+   * `store.$state`.
+   */
+  state: UnwrapRef<S>
+) => void
+
 /**
  * Context object passed to callbacks of `store.$onAction(context => {})`
  */
@@ -113,26 +213,6 @@ export type StoreOnActionListener<
   A /* extends ActionsTree */
 > = (context: StoreOnActionListenerContext<Id, S, G, A>) => void
 
-/**
- * Callback of a subscription
- */
-export type SubscriptionCallback<S> = (
-  // TODO: make type an enumeration
-  // TODO: payload should be optional
-  mutation: {
-    storeName: string
-    type: MutationType
-
-    /**
-     * DEV ONLY. Array for patch calls and single values for direct edits
-     */
-    events?: DebuggerEvent[] | DebuggerEvent
-
-    payload: DeepPartial<UnwrapRef<S>>
-  },
-  state: UnwrapRef<S>
-) => void
-
 /**
  * Base store with state and functions
  * @internal
@@ -198,14 +278,9 @@ export interface StoreWithState<
    * cleanup up when the component gets unmounted.
    *
    * @param callback - callback passed to the watcher
-   * @param onTrigger - DEV ONLY watcher debugging
-   * (https://v3.vuejs.org/guide/reactivity-computed-watchers.html#watcher-debugging)
    * @returns function that removes the watcher
    */
-  $subscribe(
-    callback: SubscriptionCallback<S>,
-    onTrigger?: (event: DebuggerEvent) => void
-  ): () => void
+  $subscribe(callback: SubscriptionCallback<S>): () => void
 
   /**
    * Array of registered action subscriptions.