fix(reactivity): ensure multiple effectScope on() and off() calls maintains correct active scope

close #12631
close #12632

This is a combination of changes from both 8dec243 and #12641
This commit is contained in:
Evan You 2025-01-08 18:07:44 +08:00
parent e8e842241a
commit 22dcbf3e20
No known key found for this signature in database
GPG Key ID: 00E9AB7A6704CE0A
2 changed files with 41 additions and 2 deletions

View File

@ -8,6 +8,10 @@ export class EffectScope {
* @internal
*/
private _active = true
/**
* @internal track `on` calls, allow `on` call multiple times
*/
private _on = 0
/**
* @internal
*/
@ -99,20 +103,27 @@ export class EffectScope {
}
}
prevScope: EffectScope | undefined
/**
* This should only be called on non-detached scopes
* @internal
*/
on(): void {
if (++this._on === 1) {
this.prevScope = activeEffectScope
activeEffectScope = this
}
}
/**
* This should only be called on non-detached scopes
* @internal
*/
off(): void {
activeEffectScope = this.parent
if (this._on > 0 && --this._on === 0) {
activeEffectScope = this.prevScope
this.prevScope = undefined
}
}
stop(fromParent?: boolean): void {

View File

@ -31,6 +31,7 @@ import {
TrackOpTypes,
TriggerOpTypes,
effectScope,
onScopeDispose,
shallowReactive,
shallowRef,
toRef,
@ -1982,4 +1983,31 @@ describe('api: watch', () => {
expect(spy1).toHaveBeenCalled()
expect(spy2).toHaveBeenCalled()
})
// #12631
test('this.$watch w/ onScopeDispose', () => {
const onCleanup = vi.fn()
const toggle = ref(true)
const Comp = defineComponent({
render() {},
created(this: any) {
this.$watch(
() => 1,
function () {},
)
onScopeDispose(onCleanup)
},
})
const App = defineComponent({
render() {
return toggle.value ? h(Comp) : null
},
})
const root = nodeOps.createElement('div')
createApp(App).mount(root)
expect(onCleanup).toBeCalledTimes(0)
})
})