@@ -8,6 +8,22 @@ export interface EngineHooks {
88
99export type CleanupRegistrar = ( cleanup : ( ) => void ) => void ;
1010
11+ type OnEffectInvalidatedHook = EngineHooks [ "onEffectInvalidated" ] ;
12+ type OnReactiveSettledHook = EngineHooks [ "onReactiveSettled" ] ;
13+
14+ const EFFECT_INVALIDATED_HOOK = 1 ;
15+ const REACTIVE_SETTLED_HOOK = 1 << 1 ;
16+
17+ function normalizeOwnHook < T extends keyof EngineHooks > (
18+ hooks : EngineHooks ,
19+ key : T ,
20+ ) : EngineHooks [ T ] | undefined {
21+ if ( ! Object . hasOwn ( hooks , key ) ) return undefined ;
22+
23+ const hook = hooks [ key ] ;
24+ return typeof hook === "function" ? hook : undefined ;
25+ }
26+
1127/**
1228 * ExecutionContext управляет состоянием вычисления и уведомлениями host'у.
1329 *
@@ -26,30 +42,57 @@ export class ExecutionContext {
2642 propagationDepth = 0 ;
2743 cleanupRegistrar : CleanupRegistrar | null = null ;
2844 readonly hooks : EngineHooks ;
45+ onEffectInvalidatedHook : OnEffectInvalidatedHook = undefined ;
46+ onReactiveSettledHook : OnReactiveSettledHook = undefined ;
47+ private hookMask = 0 ;
2948
3049 constructor ( hooks : EngineHooks = { } ) {
3150 this . hooks = { } ;
51+ // Keep the public hook snapshot and the hot-path caches synchronized.
52+ Object . defineProperties ( this . hooks , {
53+ onEffectInvalidated : {
54+ enumerable : true ,
55+ get : ( ) => this . onEffectInvalidatedHook ,
56+ set : ( hook : OnEffectInvalidatedHook ) => {
57+ this . setOnEffectInvalidatedHook ( hook ) ;
58+ } ,
59+ } ,
60+ onReactiveSettled : {
61+ enumerable : true ,
62+ get : ( ) => this . onReactiveSettledHook ,
63+ set : ( hook : OnReactiveSettledHook ) => {
64+ this . setOnReactiveSettledHook ( hook ) ;
65+ } ,
66+ } ,
67+ } ) ;
3268 this . setHooks ( hooks ) ;
3369 }
3470
3571 dispatchWatcherEvent ( node : ReactiveNode ) : void {
72+ const hook = this . onEffectInvalidatedHook ;
73+
3674 if ( __DEV__ ) {
3775 recordDebugEvent ( this , "watcher:invalidated" , {
3876 node,
3977 } ) ;
78+ } else if ( hook === undefined ) {
79+ return ;
4080 }
4181
42- this . hooks . onEffectInvalidated ?.( node ) ;
82+ hook ?.( node ) ;
4383 }
4484
4585 maybeNotifySettled ( ) : void {
46- if ( this . propagationDepth === 0 && this . activeComputed === null ) {
47- if ( __DEV__ ) {
48- recordDebugEvent ( this , "context:settled" ) ;
49- }
86+ if ( ! __DEV__ && ( this . hookMask & REACTIVE_SETTLED_HOOK ) === 0 ) return ;
87+ if ( this . propagationDepth !== 0 || this . activeComputed !== null ) return ;
5088
51- this . hooks . onReactiveSettled ?.( ) ;
89+ const hook = this . onReactiveSettledHook ;
90+
91+ if ( __DEV__ ) {
92+ recordDebugEvent ( this , "context:settled" ) ;
5293 }
94+
95+ hook ?.( ) ;
5396 }
5497
5598 enterPropagation ( ) : void {
@@ -87,38 +130,23 @@ export class ExecutionContext {
87130 }
88131
89132 setHooks ( hooks : EngineHooks = { } ) : void {
90- const onEffectInvalidated = Object . hasOwn ( hooks , "onEffectInvalidated" )
91- ? hooks . onEffectInvalidated
92- : undefined ;
93- const onReactiveSettled = Object . hasOwn ( hooks , "onReactiveSettled" )
94- ? hooks . onReactiveSettled
95- : undefined ;
96-
97- if ( typeof onEffectInvalidated === "function" ) {
98- this . hooks . onEffectInvalidated = onEffectInvalidated ;
99- } else {
100- this . hooks . onEffectInvalidated = undefined ;
101- }
133+ const onEffectInvalidated = normalizeOwnHook ( hooks , "onEffectInvalidated" ) ;
134+ const onReactiveSettled = normalizeOwnHook ( hooks , "onReactiveSettled" ) ;
102135
103- if ( typeof onReactiveSettled === "function" ) {
104- this . hooks . onReactiveSettled = onReactiveSettled ;
105- } else {
106- this . hooks . onReactiveSettled = undefined ;
107- }
136+ this . hooks . onEffectInvalidated = onEffectInvalidated ;
137+ this . hooks . onReactiveSettled = onReactiveSettled ;
108138
109139 if ( __DEV__ ) {
110140 recordDebugEvent ( this , "context:hooks" , {
111141 detail : {
112- hasOnEffectInvalidated :
113- typeof this . hooks . onEffectInvalidated === "function" ,
114- hasOnReactiveSettled :
115- typeof this . hooks . onReactiveSettled === "function" ,
142+ hasOnEffectInvalidated : this . onEffectInvalidatedHook !== undefined ,
143+ hasOnReactiveSettled : this . onReactiveSettledHook !== undefined ,
116144 } ,
117145 } ) ;
118146 }
119147 }
120148
121- registerEffectCleanup ( cleanup : ( ) => void ) : void {
149+ registerWatcherCleanup ( cleanup : ( ) => void ) : void {
122150 this . cleanupRegistrar ?.( cleanup ) ;
123151 }
124152
@@ -135,6 +163,28 @@ export class ExecutionContext {
135163 this . cleanupRegistrar = previousRegistrar ;
136164 }
137165 }
166+
167+ private setOnEffectInvalidatedHook ( hook : OnEffectInvalidatedHook ) : void {
168+ this . onEffectInvalidatedHook =
169+ typeof hook === "function" ? hook : undefined ;
170+ this . updateHookMask (
171+ EFFECT_INVALIDATED_HOOK ,
172+ this . onEffectInvalidatedHook !== undefined ,
173+ ) ;
174+ }
175+
176+ private setOnReactiveSettledHook ( hook : OnReactiveSettledHook ) : void {
177+ this . onReactiveSettledHook =
178+ typeof hook === "function" ? hook : undefined ;
179+ this . updateHookMask (
180+ REACTIVE_SETTLED_HOOK ,
181+ this . onReactiveSettledHook !== undefined ,
182+ ) ;
183+ }
184+
185+ private updateHookMask ( bit : number , enabled : boolean ) : void {
186+ this . hookMask = enabled ? this . hookMask | bit : this . hookMask & ~ bit ;
187+ }
138188}
139189
140190/**
0 commit comments