fix(watch): should not fire pre watcher on child component unmount (#7181)

close #7030
This commit is contained in:
Rudy 2023-12-08 12:24:44 +08:00 committed by GitHub
parent cdac12161e
commit 6784f0b1f8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 97 additions and 1 deletions

View File

@ -549,6 +549,98 @@ describe('api: watch', () => {
expect(cb).not.toHaveBeenCalled()
})
// #7030
it('should not fire on child component unmount w/ flush: pre', async () => {
const visible = ref(true)
const cb = vi.fn()
const Parent = defineComponent({
props: ['visible'],
render() {
return visible.value ? h(Comp) : null
}
})
const Comp = {
setup() {
watch(visible, cb, { flush: 'pre' })
},
render() {}
}
const App = {
render() {
return h(Parent, {
visible: visible.value
})
}
}
render(h(App), nodeOps.createElement('div'))
expect(cb).not.toHaveBeenCalled()
visible.value = false
await nextTick()
expect(cb).not.toHaveBeenCalled()
})
// #7030
it('flush: pre watcher in child component should not fire before parent update', async () => {
const b = ref(0)
const calls: string[] = []
const Comp = {
setup() {
watch(
() => b.value,
val => {
calls.push('watcher child')
},
{ flush: 'pre' }
)
return () => {
b.value
calls.push('render child')
}
}
}
const Parent = {
props: ['a'],
setup() {
watch(
() => b.value,
val => {
calls.push('watcher parent')
},
{ flush: 'pre' }
)
return () => {
b.value
calls.push('render parent')
return h(Comp)
}
}
}
const App = {
render() {
return h(Parent, {
a: b.value
})
}
}
render(h(App), nodeOps.createElement('div'))
expect(calls).toEqual(['render parent', 'render child'])
b.value++
await nextTick()
expect(calls).toEqual([
'render parent',
'render child',
'watcher parent',
'render parent',
'watcher child',
'render child'
])
})
// #1763
it('flush: pre watcher watching props should fire before child update', async () => {
const a = ref(0)

View File

@ -1582,7 +1582,7 @@ function baseCreateRenderer(
pauseTracking()
// props update may have triggered pre-flush watchers.
// flush them before the render update.
flushPreFlushCbs()
flushPreFlushCbs(instance)
resetTracking()
}

View File

@ -139,6 +139,7 @@ export function queuePostFlushCb(cb: SchedulerJobs) {
}
export function flushPreFlushCbs(
instance?: ComponentInternalInstance,
seen?: CountMap,
// if currently flushing, skip the current job itself
i = isFlushing ? flushIndex + 1 : 0
@ -149,6 +150,9 @@ export function flushPreFlushCbs(
for (; i < queue.length; i++) {
const cb = queue[i]
if (cb && cb.pre) {
if (instance && cb.id !== instance.uid) {
continue
}
if (__DEV__ && checkRecursiveUpdates(seen!, cb)) {
continue
}