Переглянути джерело

feat: subscribeAction (#960)

Allows to listen dispatched actions
Daniel Almaguer 7 роки тому
батько
коміт
a8326b1bd7
3 змінених файлів з 61 додано та 11 видалено
  1. 22 10
      src/store.js
  2. 13 1
      test/unit/modules.spec.js
  3. 26 0
      test/unit/store.spec.js

+ 22 - 10
src/store.js

@@ -35,6 +35,7 @@ export class Store {
     // store internal state
     // store internal state
     this._committing = false
     this._committing = false
     this._actions = Object.create(null)
     this._actions = Object.create(null)
+    this._actionSubscribers = []
     this._mutations = Object.create(null)
     this._mutations = Object.create(null)
     this._wrappedGetters = Object.create(null)
     this._wrappedGetters = Object.create(null)
     this._modules = new ModuleCollection(options)
     this._modules = new ModuleCollection(options)
@@ -123,6 +124,7 @@ export class Store {
       payload
       payload
     } = unifyObjectStyle(_type, _payload)
     } = unifyObjectStyle(_type, _payload)
 
 
+    const action = { type, payload }
     const entry = this._actions[type]
     const entry = this._actions[type]
     if (!entry) {
     if (!entry) {
       if (process.env.NODE_ENV !== 'production') {
       if (process.env.NODE_ENV !== 'production') {
@@ -130,22 +132,20 @@ export class Store {
       }
       }
       return
       return
     }
     }
+
+    this._actionSubscribers.forEach(sub => sub(action, this.state))
+
     return entry.length > 1
     return entry.length > 1
       ? Promise.all(entry.map(handler => handler(payload)))
       ? Promise.all(entry.map(handler => handler(payload)))
       : entry[0](payload)
       : entry[0](payload)
   }
   }
 
 
   subscribe (fn) {
   subscribe (fn) {
-    const subs = this._subscribers
-    if (subs.indexOf(fn) < 0) {
-      subs.push(fn)
-    }
-    return () => {
-      const i = subs.indexOf(fn)
-      if (i > -1) {
-        subs.splice(i, 1)
-      }
-    }
+    return genericSubscribe(fn, this._subscribers)
+  }
+
+  subscribeAction (fn) {
+    return genericSubscribe(fn, this._actionSubscribers)
   }
   }
 
 
   watch (getter, cb, options) {
   watch (getter, cb, options) {
@@ -203,6 +203,18 @@ export class Store {
   }
   }
 }
 }
 
 
+function genericSubscribe (fn, subs) {
+  if (subs.indexOf(fn) < 0) {
+    subs.push(fn)
+  }
+  return () => {
+    const i = subs.indexOf(fn)
+    if (i > -1) {
+      subs.splice(i, 1)
+    }
+  }
+}
+
 function resetStore (store, hot) {
 function resetStore (store, hot) {
   store._actions = Object.create(null)
   store._actions = Object.create(null)
   store._mutations = Object.create(null)
   store._mutations = Object.create(null)

+ 13 - 1
test/unit/modules.spec.js

@@ -630,7 +630,9 @@ describe('Modules', () => {
 
 
     it('plugins', function () {
     it('plugins', function () {
       let initState
       let initState
+      const actionSpy = jasmine.createSpy()
       const mutations = []
       const mutations = []
+      const subscribeActionSpy = jasmine.createSpy()
       const store = new Vuex.Store({
       const store = new Vuex.Store({
         state: {
         state: {
           a: 1
           a: 1
@@ -640,21 +642,31 @@ describe('Modules', () => {
             state.a += n
             state.a += n
           }
           }
         },
         },
+        actions: {
+          [TEST]: actionSpy
+        },
         plugins: [
         plugins: [
           store => {
           store => {
             initState = store.state
             initState = store.state
             store.subscribe((mut, state) => {
             store.subscribe((mut, state) => {
-              expect(state).toBe(store.state)
+              expect(state).toBe(state)
               mutations.push(mut)
               mutations.push(mut)
             })
             })
+            store.subscribeAction(subscribeActionSpy)
           }
           }
         ]
         ]
       })
       })
       expect(initState).toBe(store.state)
       expect(initState).toBe(store.state)
       store.commit(TEST, 2)
       store.commit(TEST, 2)
+      store.dispatch(TEST, 2)
       expect(mutations.length).toBe(1)
       expect(mutations.length).toBe(1)
       expect(mutations[0].type).toBe(TEST)
       expect(mutations[0].type).toBe(TEST)
       expect(mutations[0].payload).toBe(2)
       expect(mutations[0].payload).toBe(2)
+      expect(actionSpy).toHaveBeenCalled()
+      expect(subscribeActionSpy).toHaveBeenCalledWith(
+        { type: TEST, payload: 2 },
+        store.state
+      )
     })
     })
   })
   })
 
 

+ 26 - 0
test/unit/store.spec.js

@@ -286,6 +286,32 @@ describe('Store', () => {
     expect(store.state.a).toBe(3)
     expect(store.state.a).toBe(3)
   })
   })
 
 
+  it('subscribe: should handle subscriptions / unsubscriptions', () => {
+    const subscribeSpy = jasmine.createSpy()
+    const secondSubscribeSpy = jasmine.createSpy()
+    const testPayload = 2
+    const store = new Vuex.Store({
+      state: {},
+      mutations: {
+        [TEST]: () => {}
+      }
+    })
+
+    const unsubscribe = store.subscribe(subscribeSpy)
+    store.subscribe(secondSubscribeSpy)
+    store.commit(TEST, testPayload)
+    unsubscribe()
+    store.commit(TEST, testPayload)
+
+    expect(subscribeSpy).toHaveBeenCalledWith(
+      { type: TEST, payload: testPayload },
+      store.state
+    )
+    expect(secondSubscribeSpy).toHaveBeenCalled()
+    expect(subscribeSpy.calls.count()).toBe(1)
+    expect(secondSubscribeSpy.calls.count()).toBe(2)
+  })
+
   // store.watch should only be asserted in non-SSR environment
   // store.watch should only be asserted in non-SSR environment
   if (!isSSR) {
   if (!isSSR) {
     it('strict mode: warn mutations outside of handlers', () => {
     it('strict mode: warn mutations outside of handlers', () => {