Skip to content

Commit d3f5e6e

Browse files
authoredOct 3, 2024
fix(reactivity): prevent overwriting next property during batch processing (#12075)
close #12072
1 parent 29de6f8 commit d3f5e6e

File tree

3 files changed

+37
-15
lines changed

3 files changed

+37
-15
lines changed
 

‎packages/reactivity/__tests__/watch.spec.ts

+17
Original file line numberDiff line numberDiff line change
@@ -260,4 +260,21 @@ describe('watch', () => {
260260
src.value = 10
261261
expect(spy).toHaveBeenCalledTimes(2)
262262
})
263+
264+
test('should ensure correct execution order in batch processing', () => {
265+
const dummy: number[] = []
266+
const n1 = ref(0)
267+
const n2 = ref(0)
268+
const sum = computed(() => n1.value + n2.value)
269+
watch(n1, () => {
270+
dummy.push(1)
271+
n2.value++
272+
})
273+
watch(sum, () => dummy.push(2))
274+
watch(n1, () => dummy.push(3))
275+
276+
n1.value++
277+
278+
expect(dummy).toEqual([1, 2, 3])
279+
})
263280
})

‎packages/reactivity/src/computed.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ export class ComputedRefImpl<T = any> implements Subscriber {
121121
// avoid infinite self recursion
122122
activeSub !== this
123123
) {
124-
batch(this)
124+
batch(this, true)
125125
return true
126126
} else if (__DEV__) {
127127
// TODO warn

‎packages/reactivity/src/effect.ts

+19-14
Original file line numberDiff line numberDiff line change
@@ -234,9 +234,15 @@ export class ReactiveEffect<T = any>
234234

235235
let batchDepth = 0
236236
let batchedSub: Subscriber | undefined
237+
let batchedComputed: Subscriber | undefined
237238

238-
export function batch(sub: Subscriber): void {
239+
export function batch(sub: Subscriber, isComputed = false): void {
239240
sub.flags |= EffectFlags.NOTIFIED
241+
if (isComputed) {
242+
sub.next = batchedComputed
243+
batchedComputed = sub
244+
return
245+
}
240246
sub.next = batchedSub
241247
batchedSub = sub
242248
}
@@ -257,24 +263,23 @@ export function endBatch(): void {
257263
return
258264
}
259265

266+
if (batchedComputed) {
267+
let e: Subscriber | undefined = batchedComputed
268+
batchedComputed = undefined
269+
while (e) {
270+
const next: Subscriber | undefined = e.next
271+
e.next = undefined
272+
e.flags &= ~EffectFlags.NOTIFIED
273+
e = next
274+
}
275+
}
276+
260277
let error: unknown
261278
while (batchedSub) {
262279
let e: Subscriber | undefined = batchedSub
263-
let next: Subscriber | undefined
264-
// 1st pass: clear notified flags for computed upfront
265-
// we use the ACTIVE flag as a discriminator between computed and effect,
266-
// since NOTIFIED is useless for an inactive effect anyway.
267-
while (e) {
268-
if (!(e.flags & EffectFlags.ACTIVE)) {
269-
e.flags &= ~EffectFlags.NOTIFIED
270-
}
271-
e = e.next
272-
}
273-
e = batchedSub
274280
batchedSub = undefined
275-
// 2nd pass: run effects
276281
while (e) {
277-
next = e.next
282+
const next: Subscriber | undefined = e.next
278283
e.next = undefined
279284
e.flags &= ~EffectFlags.NOTIFIED
280285
if (e.flags & EffectFlags.ACTIVE) {

0 commit comments

Comments
 (0)
Please sign in to comment.