From cdba92e1c8139a01676abb1a874503b5f51b78e3 Mon Sep 17 00:00:00 2001 From: Wael Al-Sallami Date: Tue, 2 Jan 2018 16:52:30 -0800 Subject: [PATCH 1/4] allow action subscribers to specify before/after hooks (#1098) Action subscribers are called before the action by default. This allows them to specify before and after subscribers where the after subscriber is called when the action resolves --- src/store.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/store.js b/src/store.js index 55edd9ea9..7c98c03ac 100644 --- a/src/store.js +++ b/src/store.js @@ -128,11 +128,19 @@ export class Store { return } - this._actionSubscribers.forEach(sub => sub(action, this.state)) + this._actionSubscribers + .filter(sub => sub.before) + .forEach(sub => sub.before(action, this.state)) - return entry.length > 1 + const result = entry.length > 1 ? Promise.all(entry.map(handler => handler(payload))) : entry[0](payload) + + result.then(() => this._actionSubscribers + .filter(sub => sub.after) + .forEach(sub => sub.after(action, this.state))) + + return result } subscribe (fn) { @@ -140,7 +148,8 @@ export class Store { } subscribeAction (fn) { - return genericSubscribe(fn, this._actionSubscribers) + const subs = typeof fn === 'function' ? { before: fn } : fn + return genericSubscribe(subs, this._actionSubscribers) } watch (getter, cb, options) { From 571fad9fc59d8ea0d6ae17c2b5b8bab5ed20f894 Mon Sep 17 00:00:00 2001 From: Wael Al-Sallami Date: Tue, 2 Jan 2018 17:28:45 -0800 Subject: [PATCH 2/4] add test cases for the new before/after action subscribers (#1098) make sure that the before subscriber is called before the action, while the after subscriber is called after it resolves --- test/unit/modules.spec.js | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/test/unit/modules.spec.js b/test/unit/modules.spec.js index bc8fa2247..3d0b11d4b 100644 --- a/test/unit/modules.spec.js +++ b/test/unit/modules.spec.js @@ -668,6 +668,37 @@ describe('Modules', () => { store.state ) }) + + it('action before/after subscribers', (done) => { + const beforeSpy = jasmine.createSpy() + const afterSpy = jasmine.createSpy() + const store = new Vuex.Store({ + actions: { + [TEST]: () => new Promise(resolve => resolve()) + }, + plugins: [ + store => { + store.subscribeAction({ + before: beforeSpy, + after: afterSpy + }) + } + ] + }) + store.dispatch(TEST, 2) + expect(beforeSpy).toHaveBeenCalledWith( + { type: TEST, payload: 2 }, + store.state + ) + expect(afterSpy).not.toHaveBeenCalled() + Vue.nextTick(() => { + expect(afterSpy).toHaveBeenCalledWith( + { type: TEST, payload: 2 }, + store.state + ) + done() + }) + }) }) it('asserts a mutation should be a function', () => { From 5caff60ce74bc1e08175891c8dcc8b780369e030 Mon Sep 17 00:00:00 2001 From: Wael Al-Sallami Date: Wed, 3 Jan 2018 12:02:57 -0800 Subject: [PATCH 3/4] Replace Promise initialization with shorter form (#1098) --- test/unit/modules.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/modules.spec.js b/test/unit/modules.spec.js index 3d0b11d4b..a776fb582 100644 --- a/test/unit/modules.spec.js +++ b/test/unit/modules.spec.js @@ -674,7 +674,7 @@ describe('Modules', () => { const afterSpy = jasmine.createSpy() const store = new Vuex.Store({ actions: { - [TEST]: () => new Promise(resolve => resolve()) + [TEST]: () => Promise.resolve() }, plugins: [ store => { From d80a16f6db789b665e0cf16a7dec59d063380771 Mon Sep 17 00:00:00 2001 From: Wael Al-Sallami Date: Wed, 3 Jan 2018 12:57:28 -0800 Subject: [PATCH 4/4] Update subscribeAction type declaration and add type tests (#1098) Generalize subscribeAction's type declaration to accept both a function or an object and add type tests for that --- types/index.d.ts | 11 ++++++++++- types/test/index.ts | 35 ++++++++++++++++++++++++++++++++--- 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/types/index.d.ts b/types/index.d.ts index 70da30c1e..b5363cbc8 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -17,7 +17,7 @@ export declare class Store { commit: Commit; subscribe

(fn: (mutation: P, state: S) => any): () => void; - subscribeAction

(fn: (action: P, state: S) => any): () => void; + subscribeAction

(fn: SubscribeActionOptions): () => void; watch(getter: (state: S, getters: any) => T, cb: (value: T, oldValue: T) => void, options?: WatchOptions): () => void; registerModule(path: string, module: Module, options?: ModuleOptions): void; @@ -67,6 +67,15 @@ export interface ActionPayload extends Payload { payload: any; } +export type ActionSubscriber = (action: P, state: S) => any; + +export interface ActionSubscribersObject { + before?: ActionSubscriber; + after?: ActionSubscriber; +} + +export type SubscribeActionOptions = ActionSubscriber | ActionSubscribersObject; + export interface DispatchOptions { root?: boolean; } diff --git a/types/test/index.ts b/types/test/index.ts index 0253e5df8..0befc7fe9 100644 --- a/types/test/index.ts +++ b/types/test/index.ts @@ -39,12 +39,41 @@ namespace StoreInstance { state.value; }); - store.subscribeAction((mutation, state) => { - mutation.type; - mutation.payload; + store.subscribeAction((action, state) => { + action.type; + action.payload; state.value; }); + store.subscribeAction({ + before(action, state) { + action.type; + action.payload; + state.value; + } + }); + + store.subscribeAction({ + before(action, state) { + action.type; + action.payload; + state.value; + }, + after(action, state) { + action.type; + action.payload; + state.value; + } + }); + + store.subscribeAction({ + after(action, state) { + action.type; + action.payload; + state.value; + } + }); + store.replaceState({ value: 10 }); }