Skip to content

Commit 8a87074

Browse files
committed
fix(runtime-core/scheduler): avoid duplicate updates of child component
1 parent 778f3a5 commit 8a87074

File tree

3 files changed

+45
-3
lines changed

3 files changed

+45
-3
lines changed

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

+25-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
import { queueJob, nextTick, queuePostFlushCb } from '../src/scheduler'
1+
import {
2+
queueJob,
3+
nextTick,
4+
queuePostFlushCb,
5+
invalidateJob
6+
} from '../src/scheduler'
27

38
describe('scheduler', () => {
49
it('nextTick', async () => {
@@ -230,4 +235,23 @@ describe('scheduler', () => {
230235
expect(calls).toEqual(['job1', 'job2', 'cb1', 'cb2'])
231236
})
232237
})
238+
239+
test('invalidateJob', async () => {
240+
const calls: string[] = []
241+
const job1 = () => {
242+
calls.push('job1')
243+
invalidateJob(job2)
244+
job2()
245+
}
246+
const job2 = () => {
247+
calls.push('job2')
248+
}
249+
// queue both jobs
250+
queueJob(job1)
251+
queueJob(job2)
252+
expect(calls).toEqual([])
253+
await nextTick()
254+
// job2 should be called only once
255+
expect(calls).toEqual(['job1', 'job2'])
256+
})
233257
})

packages/runtime-core/src/renderer.ts

+9-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,12 @@ import {
3030
isFunction,
3131
PatchFlags
3232
} from '@vue/shared'
33-
import { queueJob, queuePostFlushCb, flushPostFlushCbs } from './scheduler'
33+
import {
34+
queueJob,
35+
queuePostFlushCb,
36+
flushPostFlushCbs,
37+
invalidateJob
38+
} from './scheduler'
3439
import {
3540
effect,
3641
stop,
@@ -895,6 +900,9 @@ export function createRenderer<
895900
} else {
896901
// normal update
897902
instance.next = n2
903+
// in case the child component is also queued, remove it to avoid
904+
// double updating the same child component in the same flush.
905+
invalidateJob(instance.update)
898906
// instance.update is the reactive effect runner.
899907
instance.update()
900908
}

packages/runtime-core/src/scheduler.ts

+11-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { ErrorCodes, callWithErrorHandling } from './errorHandling'
22
import { isArray } from '@vue/shared'
33

4-
const queue: Function[] = []
4+
const queue: (Function | null)[] = []
55
const postFlushCbs: Function[] = []
66
const p = Promise.resolve()
77

@@ -22,6 +22,13 @@ export function queueJob(job: () => void) {
2222
}
2323
}
2424

25+
export function invalidateJob(job: () => void) {
26+
const i = queue.indexOf(job)
27+
if (i > -1) {
28+
queue[i] = null
29+
}
30+
}
31+
2532
export function queuePostFlushCb(cb: Function | Function[]) {
2633
if (!isArray(cb)) {
2734
postFlushCbs.push(cb)
@@ -64,6 +71,9 @@ function flushJobs(seen?: CountMap) {
6471
seen = seen || new Map()
6572
}
6673
while ((job = queue.shift())) {
74+
if (job === null) {
75+
continue
76+
}
6777
if (__DEV__) {
6878
checkRecursiveUpdates(seen!, job)
6979
}

0 commit comments

Comments
 (0)