From: Eduardo San Martin Morote Date: Thu, 13 May 2021 09:37:18 +0000 (+0200) Subject: refactor(subscribe): better types X-Git-Tag: v2.0.0-alpha.17~11 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c724ae0fb1872c45da883e455ffcffb21e7887a3;p=thirdparty%2Fvuejs%2Fpinia.git refactor(subscribe): better types --- diff --git a/__tests__/store.spec.ts b/__tests__/store.spec.ts index 60f44956..35d0068d 100644 --- a/__tests__/store.spec.ts +++ b/__tests__/store.spec.ts @@ -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({ diff --git a/__tests__/subscriptions.spec.ts b/__tests__/subscriptions.spec.ts index 6d9a3c6d..b07aefc3 100644 --- a/__tests__/subscriptions.spec.ts +++ b/__tests__/subscriptions.spec.ts @@ -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', () => { diff --git a/src/index.ts b/src/index.ts index cd8d286b..27892b70 100644 --- a/src/index.ts +++ b/src/index.ts @@ -16,6 +16,7 @@ export type { StoreWithState, StoreOnActionListener, StoreOnActionListenerContext, + SubscriptionCallback, PiniaCustomProperties, DefineStoreOptions, } from './types' diff --git a/src/store.ts b/src/store.ts index 6b1cc2cc..1f459b89 100644 --- a/src/store.ts +++ b/src/store.ts @@ -30,6 +30,7 @@ import { StoreOnActionListener, UnwrapPromise, ActionsTree, + SubscriptionCallbackMutation, } from './types' import { getActivePinia, @@ -122,8 +123,7 @@ function initStore< function $patch( partialStateOrMutator: DeepPartial | ((state: S) => void) ): void { - let partialState: DeepPartial = {} - let type: MutationType + let subscriptionMutation: SubscriptionCallbackMutation 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 - ) + callback(subscriptionMutation, pinia.state.value[$id] as UnwrapRef) }) } @@ -180,9 +187,9 @@ function initStore< callback( { storeName: $id, + storeId: $id, type: MutationType.direct, - payload: {}, - events: debuggerEvents, + events: debuggerEvents as DebuggerEvent, }, state ) diff --git a/src/types.ts b/src/types.ts index 8aaed361..66c4d6f0 100644 --- a/src/types.ts +++ b/src/types.ts @@ -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 + extends _SubscriptionCallbackMutationBase { + type: MutationType.patchObject + + /** + * DEV ONLY. Array for patch calls. + */ + events: DebuggerEvent[] + + /** + * Object passed to `store.$patch()`. + */ + payload: DeepPartial +} + +/** + * 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> +} + +/** + * Context object passed to a subscription callback. + */ +export type SubscriptionCallbackMutation = + | SubscriptionCallbackMutationDirect + | SubscriptionCallbackMutationPatchObject + | SubscriptionCallbackMutationPatchFunction + export type UnwrapPromise = T extends Promise ? V : T +/** + * Callback of a subscription + */ +export type SubscriptionCallback = ( + /** + * Object with information relative to the store mutation that triggered the + * subscription. + */ + mutation: SubscriptionCallbackMutation, + + /** + * State of the store when the subscription is triggered. Same as + * `store.$state`. + */ + state: UnwrapRef +) => void + /** * Context object passed to callbacks of `store.$onAction(context => {})` */ @@ -113,26 +213,6 @@ export type StoreOnActionListener< A /* extends ActionsTree */ > = (context: StoreOnActionListenerContext) => void -/** - * Callback of a subscription - */ -export type SubscriptionCallback = ( - // 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> - }, - state: UnwrapRef -) => 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, - onTrigger?: (event: DebuggerEvent) => void - ): () => void + $subscribe(callback: SubscriptionCallback): () => void /** * Array of registered action subscriptions.