Skip to content

Commit 39e1ba7

Browse files
elbomanchristopherthielen
authored andcommitted
fix(transitionHook): Prevent queued hookFn to be called if deregistered (#2939)
* fix(transitionHook): Prevent queued hookFn to be called if deregistered Fix a bug where queued hooks are called even if they are deregistered * fix(transitionHook): Update tests for API change Closes #2928
1 parent 696148f commit 39e1ba7

File tree

5 files changed

+14
-8
lines changed

5 files changed

+14
-8
lines changed

src/transition/hookBuilder.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ export class HookBuilder {
9898
return matchingNodes.map(node => {
9999
let _options = extend({ bind: hook.bind, traceData: { hookType, context: node} }, this.baseHookOptions, options);
100100
let state = _options.stateHook ? node.state : null;
101-
let transitionHook = new TransitionHook(this.transition, state, hook.callback, _options);
101+
let transitionHook = new TransitionHook(this.transition, state, hook, _options);
102102
return <HookTuple> { hook, node, transitionHook };
103103
});
104104
};

src/transition/hookRegistry.ts

+3
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,14 @@ export class EventHook implements IEventHook {
4949
matchCriteria: HookMatchCriteria;
5050
priority: number;
5151
bind: any;
52+
_deregistered: boolean;
5253

5354
constructor(matchCriteria: HookMatchCriteria, callback: HookFn, options: HookRegOptions = <any>{}) {
5455
this.callback = callback;
5556
this.matchCriteria = extend({ to: true, from: true, exiting: true, retained: true, entering: true }, matchCriteria);
5657
this.priority = options.priority || 0;
5758
this.bind = options.bind || null;
59+
this._deregistered = false;
5860
}
5961

6062
private static _matchingNodes(nodes: PathNode[], criterion: HookMatchCriterion): PathNode[] {
@@ -99,6 +101,7 @@ function makeHookRegistrationFn(hooks: ITransitionEvents, name: string): IHookRe
99101
hooks[name].push(eventHook);
100102

101103
return function deregisterEventHook() {
104+
eventHook._deregistered = true;
102105
removeFrom(hooks[name])(eventHook);
103106
};
104107
};

src/transition/interface.ts

+1
Original file line numberDiff line numberDiff line change
@@ -762,4 +762,5 @@ export interface IEventHook {
762762
priority?: number;
763763
bind?: any;
764764
matches: (treeChanges: TreeChanges) => IMatchingNodes;
765+
_deregistered: boolean;
765766
}

src/transition/transitionHook.ts

+8-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/** @module transition */ /** for typedoc */
2-
import {TransitionHookOptions, HookFn, HookResult} from "./interface";
2+
import {TransitionHookOptions, IEventHook, HookResult} from "./interface";
33
import {defaults, noop} from "../common/common";
44
import {fnToString, maxLength} from "../common/strings";
55
import {isDefined, isPromise } from "../common/predicates";
@@ -26,21 +26,23 @@ let defaultOptions: TransitionHookOptions = {
2626
export class TransitionHook {
2727
constructor(private transition: Transition,
2828
private stateContext: State,
29-
private hookFn: HookFn,
29+
private eventHook: IEventHook,
3030
private options: TransitionHookOptions) {
3131
this.options = defaults(options, defaultOptions);
3232
}
3333

3434
private isSuperseded = () => this.options.current() !== this.options.transition;
3535

3636
invokeHook(): Promise<HookResult> {
37-
let { options, hookFn } = this;
37+
let { options, eventHook } = this;
3838
trace.traceHookInvocation(this, options);
3939
if (options.rejectIfSuperseded && this.isSuperseded()) {
4040
return Rejection.superseded(options.current()).toPromise();
4141
}
4242

43-
let hookResult = hookFn.call(options.bind, this.transition, this.stateContext);
43+
let hookResult = !eventHook._deregistered
44+
? eventHook.callback.call(options.bind, this.transition, this.stateContext)
45+
: undefined;
4446
return this.handleHookResult(hookResult);
4547
}
4648

@@ -73,10 +75,10 @@ export class TransitionHook {
7375
}
7476

7577
toString() {
76-
let { options, hookFn } = this;
78+
let { options, eventHook } = this;
7779
let event = parse("traceData.hookType")(options) || "internal",
7880
context = parse("traceData.context.state.name")(options) || parse("traceData.context")(options) || "unknown",
79-
name = fnToString(hookFn);
81+
name = fnToString(eventHook.callback);
8082
return `${event} context: ${context}, ${maxLength(200, name)}`;
8183
}
8284

test/core/hookBuilderSpec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ describe('HookBuilder:', function() {
6262
expect(typeof callback).toBe('function')
6363
});
6464

65-
const getFn = x => x['hookFn'];
65+
const getFn = x => x['eventHook']['callback'];
6666

6767
describe('HookMatchCriteria', function() {
6868

0 commit comments

Comments
 (0)