Skip to content

Commit 6784f0b

Browse files
authored
fix(watch): should not fire pre watcher on child component unmount (#7181)
close #7030
1 parent cdac121 commit 6784f0b

File tree

3 files changed

+97
-1
lines changed

3 files changed

+97
-1
lines changed

packages/runtime-core/__tests__/apiWatch.spec.ts

+92
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,98 @@ describe('api: watch', () => {
549549
expect(cb).not.toHaveBeenCalled()
550550
})
551551

552+
// #7030
553+
it('should not fire on child component unmount w/ flush: pre', async () => {
554+
const visible = ref(true)
555+
const cb = vi.fn()
556+
const Parent = defineComponent({
557+
props: ['visible'],
558+
render() {
559+
return visible.value ? h(Comp) : null
560+
}
561+
})
562+
const Comp = {
563+
setup() {
564+
watch(visible, cb, { flush: 'pre' })
565+
},
566+
render() {}
567+
}
568+
const App = {
569+
render() {
570+
return h(Parent, {
571+
visible: visible.value
572+
})
573+
}
574+
}
575+
render(h(App), nodeOps.createElement('div'))
576+
expect(cb).not.toHaveBeenCalled()
577+
visible.value = false
578+
await nextTick()
579+
expect(cb).not.toHaveBeenCalled()
580+
})
581+
582+
// #7030
583+
it('flush: pre watcher in child component should not fire before parent update', async () => {
584+
const b = ref(0)
585+
const calls: string[] = []
586+
587+
const Comp = {
588+
setup() {
589+
watch(
590+
() => b.value,
591+
val => {
592+
calls.push('watcher child')
593+
},
594+
{ flush: 'pre' }
595+
)
596+
return () => {
597+
b.value
598+
calls.push('render child')
599+
}
600+
}
601+
}
602+
603+
const Parent = {
604+
props: ['a'],
605+
setup() {
606+
watch(
607+
() => b.value,
608+
val => {
609+
calls.push('watcher parent')
610+
},
611+
{ flush: 'pre' }
612+
)
613+
return () => {
614+
b.value
615+
calls.push('render parent')
616+
return h(Comp)
617+
}
618+
}
619+
}
620+
621+
const App = {
622+
render() {
623+
return h(Parent, {
624+
a: b.value
625+
})
626+
}
627+
}
628+
629+
render(h(App), nodeOps.createElement('div'))
630+
expect(calls).toEqual(['render parent', 'render child'])
631+
632+
b.value++
633+
await nextTick()
634+
expect(calls).toEqual([
635+
'render parent',
636+
'render child',
637+
'watcher parent',
638+
'render parent',
639+
'watcher child',
640+
'render child'
641+
])
642+
})
643+
552644
// #1763
553645
it('flush: pre watcher watching props should fire before child update', async () => {
554646
const a = ref(0)

packages/runtime-core/src/renderer.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1582,7 +1582,7 @@ function baseCreateRenderer(
15821582
pauseTracking()
15831583
// props update may have triggered pre-flush watchers.
15841584
// flush them before the render update.
1585-
flushPreFlushCbs()
1585+
flushPreFlushCbs(instance)
15861586
resetTracking()
15871587
}
15881588

packages/runtime-core/src/scheduler.ts

+4
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ export function queuePostFlushCb(cb: SchedulerJobs) {
139139
}
140140

141141
export function flushPreFlushCbs(
142+
instance?: ComponentInternalInstance,
142143
seen?: CountMap,
143144
// if currently flushing, skip the current job itself
144145
i = isFlushing ? flushIndex + 1 : 0
@@ -149,6 +150,9 @@ export function flushPreFlushCbs(
149150
for (; i < queue.length; i++) {
150151
const cb = queue[i]
151152
if (cb && cb.pre) {
153+
if (instance && cb.id !== instance.uid) {
154+
continue
155+
}
152156
if (__DEV__ && checkRecursiveUpdates(seen!, cb)) {
153157
continue
154158
}

0 commit comments

Comments
 (0)