it('flush: pre watcher watching props should fire before child update', async () => {
const a = ref(0)
const b = ref(0)
+ const c = ref(0)
const calls: string[] = []
const Comp = {
watch(
() => props.a + props.b,
() => {
- calls.push('watcher')
+ calls.push('watcher 1')
+ c.value++
+ },
+ { flush: 'pre' }
+ )
+
+ // #1777 chained pre-watcher
+ watch(
+ c,
+ () => {
+ calls.push('watcher 2')
},
{ flush: 'pre' }
)
return () => {
+ c.value
calls.push('render')
}
}
a.value++
b.value++
await nextTick()
- expect(calls).toEqual(['render', 'watcher', 'render'])
+ expect(calls).toEqual(['render', 'watcher 1', 'watcher 2', 'render'])
})
it('deep', async () => {
let pendingPostFlushCbs: Function[] | null = null
let pendingPostFlushIndex = 0
let hasPendingPreFlushJobs = false
+let currentPreFlushParentJob: SchedulerJob | null = null
const RECURSION_LIMIT = 100
type CountMap = Map<SchedulerJob | Function, number>
// allow it recursively trigger itself - it is the user's responsibility to
// ensure it doesn't end up in an infinite loop.
if (
- !queue.length ||
- !queue.includes(job, isFlushing && job.cb ? flushIndex + 1 : flushIndex)
+ (!queue.length ||
+ !queue.includes(
+ job,
+ isFlushing && job.cb ? flushIndex + 1 : flushIndex
+ )) &&
+ job !== currentPreFlushParentJob
) {
+ if (job.id && job.id > 0) {
+ debugger
+ }
queue.push(job)
if ((job.id as number) < 0) hasPendingPreFlushJobs = true
queueFlush()
}
}
-export function runPreflushJobs() {
+/**
+ * Run flush: 'pre' watcher callbacks. This is only called in
+ * `updateComponentPreRender` to cover the case where pre-flush watchers are
+ * triggered by the change of a component's props. This means the scheduler is
+ * already flushing and we are already inside the component's update effect,
+ * right when the render function is about to be called. So if the watcher
+ * triggers the same component to update, we don't want it to be queued (this
+ * is checked via `currentPreFlushParentJob`).
+ */
+export function runPreflushJobs(parentJob: SchedulerJob) {
if (hasPendingPreFlushJobs) {
+ currentPreFlushParentJob = parentJob
hasPendingPreFlushJobs = false
- for (let job, i = queue.length - 1; i > flushIndex; i--) {
+ for (let job, i = flushIndex + 1; i < queue.length; i++) {
job = queue[i]
if (job && (job.id as number) < 0) {
job()
queue[i] = null
}
}
+ currentPreFlushParentJob = null
}
}