fix(reactivity): effect shoud only recursively self trigger with explicit options
fix #2125
This commit is contained in:
parent
89e9ab8a2a
commit
3810de7d6b
@ -25,6 +25,7 @@ export interface ReactiveEffectOptions {
|
|||||||
onTrack?: (event: DebuggerEvent) => void
|
onTrack?: (event: DebuggerEvent) => void
|
||||||
onTrigger?: (event: DebuggerEvent) => void
|
onTrigger?: (event: DebuggerEvent) => void
|
||||||
onStop?: () => void
|
onStop?: () => void
|
||||||
|
allowRecurse?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DebuggerEvent = {
|
export type DebuggerEvent = {
|
||||||
@ -178,7 +179,11 @@ export function trigger(
|
|||||||
const effects = new Set<ReactiveEffect>()
|
const effects = new Set<ReactiveEffect>()
|
||||||
const add = (effectsToAdd: Set<ReactiveEffect> | undefined) => {
|
const add = (effectsToAdd: Set<ReactiveEffect> | undefined) => {
|
||||||
if (effectsToAdd) {
|
if (effectsToAdd) {
|
||||||
effectsToAdd.forEach(effect => effects.add(effect))
|
effectsToAdd.forEach(effect => {
|
||||||
|
if (effect !== activeEffect || effect.options.allowRecurse) {
|
||||||
|
effects.add(effect)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -779,4 +779,17 @@ describe('api: watch', () => {
|
|||||||
// should trigger now
|
// should trigger now
|
||||||
expect(sideEffect).toBe(2)
|
expect(sideEffect).toBe(2)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// #2125
|
||||||
|
test('watchEffect should not recursively trigger itself', async () => {
|
||||||
|
const spy = jest.fn()
|
||||||
|
const price = ref(10)
|
||||||
|
const history = ref<number[]>([])
|
||||||
|
watchEffect(() => {
|
||||||
|
history.value.push(price.value)
|
||||||
|
spy()
|
||||||
|
})
|
||||||
|
await nextTick()
|
||||||
|
expect(spy).toHaveBeenCalledTimes(1)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@ -5,7 +5,10 @@ import {
|
|||||||
nodeOps,
|
nodeOps,
|
||||||
serializeInner,
|
serializeInner,
|
||||||
nextTick,
|
nextTick,
|
||||||
VNode
|
VNode,
|
||||||
|
provide,
|
||||||
|
inject,
|
||||||
|
Ref
|
||||||
} from '@vue/runtime-test'
|
} from '@vue/runtime-test'
|
||||||
|
|
||||||
describe('renderer: component', () => {
|
describe('renderer: component', () => {
|
||||||
@ -104,4 +107,34 @@ describe('renderer: component', () => {
|
|||||||
)
|
)
|
||||||
expect(Comp1.updated).not.toHaveBeenCalled()
|
expect(Comp1.updated).not.toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// #2043
|
||||||
|
test('component child synchronously updating parent state should trigger parent re-render', async () => {
|
||||||
|
const App = {
|
||||||
|
setup() {
|
||||||
|
const n = ref(0)
|
||||||
|
provide('foo', n)
|
||||||
|
return () => {
|
||||||
|
return [h('div', n.value), h(Child)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Child = {
|
||||||
|
setup() {
|
||||||
|
const n = inject<Ref<number>>('foo')!
|
||||||
|
n.value++
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
return h('div', n.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const root = nodeOps.createElement('div')
|
||||||
|
render(h(App), root)
|
||||||
|
expect(serializeInner(root)).toBe(`<div>0</div><div>1</div>`)
|
||||||
|
await nextTick()
|
||||||
|
expect(serializeInner(root)).toBe(`<div>1</div><div>1</div>`)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@ -44,7 +44,6 @@ import {
|
|||||||
flushPostFlushCbs,
|
flushPostFlushCbs,
|
||||||
invalidateJob,
|
invalidateJob,
|
||||||
flushPreFlushCbs,
|
flushPreFlushCbs,
|
||||||
SchedulerJob,
|
|
||||||
SchedulerCb
|
SchedulerCb
|
||||||
} from './scheduler'
|
} from './scheduler'
|
||||||
import { effect, stop, ReactiveEffectOptions, isRef } from '@vue/reactivity'
|
import { effect, stop, ReactiveEffectOptions, isRef } from '@vue/reactivity'
|
||||||
@ -261,7 +260,9 @@ export const enum MoveType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const prodEffectOptions = {
|
const prodEffectOptions = {
|
||||||
scheduler: queueJob
|
scheduler: queueJob,
|
||||||
|
// #1801, #2043 component render effects should allow recursive updates
|
||||||
|
allowRecurse: true
|
||||||
}
|
}
|
||||||
|
|
||||||
function createDevEffectOptions(
|
function createDevEffectOptions(
|
||||||
@ -269,6 +270,7 @@ function createDevEffectOptions(
|
|||||||
): ReactiveEffectOptions {
|
): ReactiveEffectOptions {
|
||||||
return {
|
return {
|
||||||
scheduler: queueJob,
|
scheduler: queueJob,
|
||||||
|
allowRecurse: true,
|
||||||
onTrack: instance.rtc ? e => invokeArrayFns(instance.rtc!, e) : void 0,
|
onTrack: instance.rtc ? e => invokeArrayFns(instance.rtc!, e) : void 0,
|
||||||
onTrigger: instance.rtg ? e => invokeArrayFns(instance.rtg!, e) : void 0
|
onTrigger: instance.rtg ? e => invokeArrayFns(instance.rtg!, e) : void 0
|
||||||
}
|
}
|
||||||
@ -1489,8 +1491,6 @@ function baseCreateRenderer(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, __DEV__ ? createDevEffectOptions(instance) : prodEffectOptions)
|
}, __DEV__ ? createDevEffectOptions(instance) : prodEffectOptions)
|
||||||
// #1801 mark it to allow recursive updates
|
|
||||||
;(instance.update as SchedulerJob).allowRecurse = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateComponentPreRender = (
|
const updateComponentPreRender = (
|
||||||
|
Loading…
x
Reference in New Issue
Block a user