onTrack?: (event: DebuggerEvent) => void
onTrigger?: (event: DebuggerEvent) => void
onStop?: () => void
+ allowRecurse?: boolean
}
export type DebuggerEvent = {
const effects = new Set<ReactiveEffect>()
const add = (effectsToAdd: Set<ReactiveEffect> | undefined) => {
if (effectsToAdd) {
- effectsToAdd.forEach(effect => effects.add(effect))
+ effectsToAdd.forEach(effect => {
+ if (effect !== activeEffect || effect.options.allowRecurse) {
+ effects.add(effect)
+ }
+ })
}
}
// should trigger now
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)
+ })
})
nodeOps,
serializeInner,
nextTick,
- VNode
+ VNode,
+ provide,
+ inject,
+ Ref
} from '@vue/runtime-test'
describe('renderer: component', () => {
)
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>`)
+ })
})
flushPostFlushCbs,
invalidateJob,
flushPreFlushCbs,
- SchedulerJob,
SchedulerCb
} from './scheduler'
import { effect, stop, ReactiveEffectOptions, isRef } from '@vue/reactivity'
}
const prodEffectOptions = {
- scheduler: queueJob
+ scheduler: queueJob,
+ // #1801, #2043 component render effects should allow recursive updates
+ allowRecurse: true
}
function createDevEffectOptions(
): ReactiveEffectOptions {
return {
scheduler: queueJob,
+ allowRecurse: true,
onTrack: instance.rtc ? e => invokeArrayFns(instance.rtc!, e) : void 0,
onTrigger: instance.rtg ? e => invokeArrayFns(instance.rtg!, e) : void 0
}
}
}
}, __DEV__ ? createDevEffectOptions(instance) : prodEffectOptions)
- // #1801 mark it to allow recursive updates
- ;(instance.update as SchedulerJob).allowRecurse = true
}
const updateComponentPreRender = (