Skip to content

Commit 2cc5615

Browse files
committedMar 5, 2024·
feat(reactivity): onEffectCleanup API
ref #10173 Instead of exposing `getCurrentEffect`, this version accepts a second argument to suppress the no-active-effect warning.
1 parent 70196a4 commit 2cc5615

File tree

3 files changed

+84
-0
lines changed

3 files changed

+84
-0
lines changed
 

‎packages/reactivity/__tests__/effect.spec.ts

+39
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
} from '@vue/runtime-test'
2424
import {
2525
endBatch,
26+
onEffectCleanup,
2627
pauseTracking,
2728
resetTracking,
2829
startBatch,
@@ -1131,4 +1132,42 @@ describe('reactivity/effect', () => {
11311132
expect(getSubCount(depC)).toBe(1)
11321133
})
11331134
})
1135+
1136+
describe('onEffectCleanup', () => {
1137+
it('should get called correctly', async () => {
1138+
const count = ref(0)
1139+
const cleanupEffect = vi.fn()
1140+
1141+
const e = effect(() => {
1142+
onEffectCleanup(cleanupEffect)
1143+
count.value
1144+
})
1145+
1146+
count.value++
1147+
await nextTick()
1148+
expect(cleanupEffect).toHaveBeenCalledTimes(1)
1149+
1150+
count.value++
1151+
await nextTick()
1152+
expect(cleanupEffect).toHaveBeenCalledTimes(2)
1153+
1154+
// call it on stop
1155+
e.effect.stop()
1156+
expect(cleanupEffect).toHaveBeenCalledTimes(3)
1157+
})
1158+
1159+
it('should warn if called without active effect', () => {
1160+
onEffectCleanup(() => {})
1161+
expect(
1162+
`onEffectCleanup() was called when there was no active effect`,
1163+
).toHaveBeenWarned()
1164+
})
1165+
1166+
it('should not warn without active effect when failSilently argument is passed', () => {
1167+
onEffectCleanup(() => {}, true)
1168+
expect(
1169+
`onEffectCleanup() was called when there was no active effect`,
1170+
).not.toHaveBeenWarned()
1171+
})
1172+
})
11341173
})

‎packages/reactivity/src/effect.ts

+44
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,10 @@ export class ReactiveEffect<T = any>
126126
* @internal
127127
*/
128128
nextEffect?: ReactiveEffect = undefined
129+
/**
130+
* @internal
131+
*/
132+
cleanup?: () => void = undefined
129133

130134
scheduler?: EffectScheduler = undefined
131135
onStop?: () => void
@@ -165,6 +169,7 @@ export class ReactiveEffect<T = any>
165169
}
166170

167171
this.flags |= EffectFlags.RUNNING
172+
cleanupEffect(this)
168173
prepareDeps(this)
169174
const prevEffect = activeSub
170175
const prevShouldTrack = shouldTrack
@@ -193,6 +198,7 @@ export class ReactiveEffect<T = any>
193198
removeSub(link)
194199
}
195200
this.deps = this.depsTail = undefined
201+
cleanupEffect(this)
196202
this.onStop && this.onStop()
197203
this.flags &= ~EffectFlags.ACTIVE
198204
}
@@ -479,3 +485,41 @@ export function resetTracking() {
479485
const last = trackStack.pop()
480486
shouldTrack = last === undefined ? true : last
481487
}
488+
489+
/**
490+
* Registers a cleanup function for the current active effect.
491+
* The cleanup function is called right before the next effect run, or when the
492+
* effect is stopped.
493+
*
494+
* Throws a warning iff there is no currenct active effect. The warning can be
495+
* suppressed by passing `true` to the second argument.
496+
*
497+
* @param fn - the cleanup function to be registered
498+
* @param failSilently - if `true`, will not throw warning when called without
499+
* an active effect.
500+
*/
501+
export function onEffectCleanup(fn: () => void, failSilently = false) {
502+
if (activeSub instanceof ReactiveEffect) {
503+
activeSub.cleanup = fn
504+
} else if (__DEV__ && !failSilently) {
505+
warn(
506+
`onEffectCleanup() was called when there was no active effect` +
507+
` to associate with.`,
508+
)
509+
}
510+
}
511+
512+
function cleanupEffect(e: ReactiveEffect) {
513+
const { cleanup } = e
514+
e.cleanup = undefined
515+
if (cleanup) {
516+
// run cleanup without active effect
517+
const prevSub = activeSub
518+
activeSub = undefined
519+
try {
520+
cleanup()
521+
} finally {
522+
activeSub = prevSub
523+
}
524+
}
525+
}

‎packages/reactivity/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ export {
5353
enableTracking,
5454
pauseTracking,
5555
resetTracking,
56+
onEffectCleanup,
5657
ReactiveEffect,
5758
EffectFlags,
5859
type ReactiveEffectRunner,

0 commit comments

Comments
 (0)
Please sign in to comment.