fix(watch): ensure watchers respect detached scope

fix #4158
This commit is contained in:
Evan You
2021-07-20 14:32:17 -04:00
parent 2bdee50a59
commit bc7f9767f5
3 changed files with 46 additions and 15 deletions

View File

@@ -25,7 +25,9 @@ import {
import {
currentInstance,
ComponentInternalInstance,
isInSSRComponentSetup
isInSSRComponentSetup,
setCurrentInstance,
unsetCurrentInstance
} from './component'
import {
ErrorCodes,
@@ -157,8 +159,7 @@ export function watch<T = any, Immediate extends Readonly<boolean> = false>(
function doWatch(
source: WatchSource | WatchSource[] | WatchEffect | object,
cb: WatchCallback | null,
{ immediate, deep, flush, onTrack, onTrigger }: WatchOptions = EMPTY_OBJ,
instance = currentInstance
{ immediate, deep, flush, onTrack, onTrigger }: WatchOptions = EMPTY_OBJ
): WatchStopHandle {
if (__DEV__ && !cb) {
if (immediate !== undefined) {
@@ -184,6 +185,7 @@ function doWatch(
)
}
const instance = currentInstance
let getter: () => any
let forceTrigger = false
let isMultiSource = false
@@ -340,8 +342,7 @@ function doWatch(
}
}
const scope = instance && instance.scope
const effect = new ReactiveEffect(getter, scheduler, scope)
const effect = new ReactiveEffect(getter, scheduler)
if (__DEV__) {
effect.onTrack = onTrack
@@ -366,8 +367,8 @@ function doWatch(
return () => {
effect.stop()
if (scope) {
remove(scope.effects!, effect)
if (instance && instance.scope) {
remove(instance.scope.effects!, effect)
}
}
}
@@ -392,7 +393,15 @@ export function instanceWatch(
cb = value.handler as Function
options = value
}
return doWatch(getter, cb.bind(publicThis), options, this)
const cur = currentInstance
setCurrentInstance(this)
const res = doWatch(getter, cb.bind(publicThis), options)
if (cur) {
setCurrentInstance(cur)
} else {
unsetCurrentInstance()
}
return res
}
export function createPathGetter(ctx: any, path: string) {

View File

@@ -2304,9 +2304,8 @@ function baseCreateRenderer(
instance.emit('hook:beforeDestroy')
}
if (scope) {
scope.stop()
}
// stop effects in component scope
scope.stop()
// update may be null if a component is unmounted before its async
// setup has resolved.