@@ -3,6 +3,7 @@ import type { TrackOpTypes, TriggerOpTypes } from './constants'
33import { setupOnTrigger } from './debug'
44import { activeEffectScope } from './effectScope'
55import {
6+ type Dependency ,
67 type Link ,
78 type Subscriber ,
89 SubscriberFlags ,
@@ -51,26 +52,24 @@ export enum EffectFlags {
5152 ALLOW_RECURSE = 1 << 7 ,
5253 PAUSED = 1 << 8 ,
5354 NOTIFIED = 1 << 9 ,
54- STOP = 1 << 10 ,
5555}
5656
57- export class ReactiveEffect < T = any > implements ReactiveEffectOptions {
57+ export class ReactiveEffect < T = any >
58+ implements ReactiveEffectOptions , Dependency , Subscriber
59+ {
5860 // Subscriber
5961 deps : Link | undefined = undefined
6062 depsTail : Link | undefined = undefined
6163 flags : number = 0
64+ cleanups : number = 0
6265
6366 // Dependency
6467 subs : Link | undefined = undefined
6568 subsTail : Link | undefined = undefined
6669
67- /**
68- * @internal
69- */
70- cleanup ?: ( ) => void = undefined
71-
72- onStop ?: ( ) => void
70+ // dev only
7371 onTrack ?: ( event : DebuggerEvent ) => void
72+ // dev only
7473 onTrigger ?: ( event : DebuggerEvent ) => void
7574
7675 // @ts -expect-error
@@ -80,13 +79,13 @@ export class ReactiveEffect<T = any> implements ReactiveEffectOptions {
8079 if ( fn !== undefined ) {
8180 this . fn = fn
8281 }
83- if ( activeEffectScope && activeEffectScope . active ) {
82+ if ( activeEffectScope ) {
8483 link ( this , activeEffectScope )
8584 }
8685 }
8786
8887 get active ( ) : boolean {
89- return ! ( this . flags & EffectFlags . STOP )
88+ return this . deps !== undefined
9089 }
9190
9291 pause ( ) : void {
@@ -122,12 +121,10 @@ export class ReactiveEffect<T = any> implements ReactiveEffectOptions {
122121 }
123122
124123 run ( ) : T {
125- let flags = this . flags
126- if ( flags & EffectFlags . STOP ) {
127- // stopped during cleanup
128- return this . fn ( )
124+ const cleanups = this . cleanups
125+ if ( cleanups ) {
126+ cleanup ( this , cleanups )
129127 }
130- cleanupEffect ( this )
131128 const prevSub = activeSub
132129 setActiveSub ( this )
133130 startTracking ( this )
@@ -143,7 +140,7 @@ export class ReactiveEffect<T = any> implements ReactiveEffectOptions {
143140 }
144141 setActiveSub ( prevSub )
145142 endTracking ( this )
146- flags = this . flags
143+ const flags = this . flags
147144 if (
148145 ( flags & ( SubscriberFlags . Recursed | EffectFlags . ALLOW_RECURSE ) ) ===
149146 ( SubscriberFlags . Recursed | EffectFlags . ALLOW_RECURSE )
@@ -155,17 +152,15 @@ export class ReactiveEffect<T = any> implements ReactiveEffectOptions {
155152 }
156153
157154 stop ( ) : void {
158- const flags = this . flags
159- if ( ! ( flags & EffectFlags . STOP ) ) {
160- this . flags = flags | EffectFlags . STOP
161- startTracking ( this )
162- endTracking ( this )
163- cleanupEffect ( this )
164- this . onStop && this . onStop ( )
165-
166- if ( this . subs !== undefined ) {
167- unlink ( this . subs )
168- }
155+ const sub = this . subs
156+ const cleanups = this . cleanups
157+ if ( sub !== undefined ) {
158+ unlink ( sub )
159+ }
160+ startTracking ( this )
161+ endTracking ( this )
162+ if ( cleanups ) {
163+ cleanup ( this , cleanups )
169164 }
170165 }
171166
@@ -205,6 +200,15 @@ export function effect<T = any>(
205200
206201 const e = new ReactiveEffect ( fn )
207202 if ( options ) {
203+ const onStop = options . onStop
204+ if ( onStop !== undefined ) {
205+ options . onStop = undefined
206+ const stop = e . stop . bind ( e )
207+ e . stop = ( ) => {
208+ stop ( )
209+ onStop ( )
210+ }
211+ }
208212 extend ( e , options )
209213 }
210214 try {
@@ -276,6 +280,32 @@ export function resetTracking(): void {
276280 }
277281}
278282
283+ const cleanupCbs = new WeakMap ( )
284+
285+ export function onCleanup (
286+ sub : Subscriber & { cleanups : number } ,
287+ cb : ( ) => void ,
288+ ) : void {
289+ const cbs = cleanupCbs . get ( sub )
290+ if ( cbs === undefined ) {
291+ cleanupCbs . set ( sub , [ cb ] )
292+ sub . cleanups = 1
293+ } else {
294+ cbs [ sub . cleanups ! ++ ] = cb
295+ }
296+ }
297+
298+ export function cleanup (
299+ sub : Subscriber & { cleanups : number } ,
300+ length : number ,
301+ ) : void {
302+ const cbs = cleanupCbs . get ( sub ) !
303+ for ( let i = 0 ; i < length ; ++ i ) {
304+ cbs [ i ] ( )
305+ }
306+ sub . cleanups = 0
307+ }
308+
279309/**
280310 * Registers a cleanup function for the current active effect.
281311 * The cleanup function is called right before the next effect run, or when the
@@ -289,8 +319,9 @@ export function resetTracking(): void {
289319 * an active effect.
290320 */
291321export function onEffectCleanup ( fn : ( ) => void , failSilently = false ) : void {
292- if ( activeSub instanceof ReactiveEffect ) {
293- activeSub . cleanup = fn
322+ const e = activeSub
323+ if ( e instanceof ReactiveEffect ) {
324+ onCleanup ( e , ( ) => cleanupEffect ( fn ) )
294325 } else if ( __DEV__ && ! failSilently ) {
295326 warn (
296327 `onEffectCleanup() was called when there was no active effect` +
@@ -299,18 +330,14 @@ export function onEffectCleanup(fn: () => void, failSilently = false): void {
299330 }
300331}
301332
302- function cleanupEffect ( e : ReactiveEffect ) {
303- const cleanup = e . cleanup
304- if ( cleanup !== undefined ) {
305- e . cleanup = undefined
306- // run cleanup without active effect
307- const prevSub = activeSub
308- activeSub = undefined
309- try {
310- cleanup ( )
311- } finally {
312- activeSub = prevSub
313- }
333+ function cleanupEffect ( fn : ( ) => void ) {
334+ // run cleanup without active effect
335+ const prevSub = activeSub
336+ activeSub = undefined
337+ try {
338+ fn ( )
339+ } finally {
340+ activeSub = prevSub
314341 }
315342}
316343
0 commit comments