diff --git a/packages/pinia/__tests__/subscriptions.spec.ts b/packages/pinia/__tests__/subscriptions.spec.ts index 1c280a5249..fd1d44e659 100644 --- a/packages/pinia/__tests__/subscriptions.spec.ts +++ b/packages/pinia/__tests__/subscriptions.spec.ts @@ -353,5 +353,35 @@ describe('Subscriptions', () => { expect(spy1).toHaveBeenCalledTimes(1) expect(spy2).toHaveBeenCalledTimes(2) }) + + it('debugger events should not be array when subscription is not trigger by patch', () => { + const store = useStore() + store.$subscribe( + ({ type, events }) => { + if (type === MutationType.direct) { + expect(Array.isArray(events)).toBe(false) + } + }, + { flush: 'sync' } + ) + store.$patch({ user: 'Edu' }) + store.user = 'a' + }) + + it('should trigger subscription when mutate state synchronously after patch', async () => { + const store = useStore() + const spy1 = vi.fn() + const spy2 = vi.fn() + const spy3 = vi.fn() + store.$subscribe(spy1, { flush: 'sync' }) + store.$subscribe(spy2, { flush: 'pre' }) + store.$subscribe(spy3, { flush: 'post' }) + store.$patch({ user: 'Edu' }) + store.user = 'a' + expect(spy1).toHaveBeenCalledTimes(2) + await nextTick() + expect(spy2).toHaveBeenCalledTimes(2) + expect(spy3).toHaveBeenCalledTimes(2) + }) }) }) diff --git a/packages/pinia/src/store.ts b/packages/pinia/src/store.ts index a7be113ce3..3c6f440e0c 100644 --- a/packages/pinia/src/store.ts +++ b/packages/pinia/src/store.ts @@ -19,7 +19,6 @@ import { toRefs, Ref, ref, - nextTick, } from 'vue' import { StateTree, @@ -247,7 +246,7 @@ function createSetupStore< if (isListening) { debuggerEvents = event // avoid triggering this while the store is being built and the state is being set in pinia - } else if (isListening == false && !store._hotUpdating) { + } else if (isListening === false && !store._hotUpdating) { // let patch send all the events together later /* istanbul ignore else */ if (Array.isArray(debuggerEvents)) { @@ -262,8 +261,8 @@ function createSetupStore< } // internal state - let isListening: boolean // set to true at the end - let isSyncListening: boolean // set to true at the end + let isListening = false // set to true at the end + let shouldTrigger = false // The initial value does not matter, and no need to set to true at the end let subscriptions: SubscriptionCallback[] = [] let actionSubscriptions: StoreOnActionListener[] = [] let debuggerEvents: DebuggerEvent[] | DebuggerEvent @@ -278,9 +277,6 @@ function createSetupStore< const hotState = ref({} as S) - // avoid triggering too many listeners - // https://github.com/vuejs/pinia/issues/1129 - let activeListener: Symbol | undefined function $patch(stateMutation: (state: UnwrapRef) => void): void function $patch(partialState: _DeepPartial>): void function $patch( @@ -289,7 +285,7 @@ function createSetupStore< | ((state: UnwrapRef) => void) ): void { let subscriptionMutation: SubscriptionCallbackMutation - isListening = isSyncListening = false + isListening = false // reset the debugger events since patches are sync /* istanbul ignore else */ if (__DEV__) { @@ -311,13 +307,7 @@ function createSetupStore< events: debuggerEvents as DebuggerEvent[], } } - const myListenerId = (activeListener = Symbol()) - nextTick().then(() => { - if (activeListener === myListenerId) { - isListening = true - } - }) - isSyncListening = true + isListening = true // because we paused the watcher, we need to manually call the subscriptions triggerSubscriptions( subscriptions, @@ -441,11 +431,19 @@ function createSetupStore< options.detached, () => stopWatcher() ) - const stopWatcher = scope.run(() => - watch( + const stopWatcher = scope.run(() => { + const stop1 = watch( + () => pinia.state.value[$id], + () => { + shouldTrigger = isListening + }, + { deep: true, flush: 'sync' } + ) + + const stop2 = watch( () => pinia.state.value[$id] as UnwrapRef, (state) => { - if (options.flush === 'sync' ? isSyncListening : isListening) { + if (shouldTrigger) { callback( { storeId: $id, @@ -458,7 +456,14 @@ function createSetupStore< }, assign({}, $subscribeOptions, options) ) - )! + + const stop = () => { + stop1() + stop2() + } + + return stop + })! return removeSubscription }, @@ -614,12 +619,8 @@ function createSetupStore< // avoid devtools logging this as a mutation isListening = false - isSyncListening = false pinia.state.value[$id] = toRef(newStore._hmrPayload, 'hotState') - isSyncListening = true - nextTick().then(() => { - isListening = true - }) + isListening = true for (const actionName in newStore._hmrPayload.actions) { const actionFn: _Method = newStore[actionName] @@ -748,7 +749,6 @@ function createSetupStore< } isListening = true - isSyncListening = true return store }