Skip to content

Commit d87d059

Browse files
committed
feat(runtime-core): watchSyncEffect
1 parent 33c2fbf commit d87d059

File tree

3 files changed

+64
-3
lines changed

3 files changed

+64
-3
lines changed

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

+45-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ import {
1616
serializeInner,
1717
TestElement,
1818
h,
19-
createApp
19+
createApp,
20+
watchPostEffect,
21+
watchSyncEffect
2022
} from '@vue/runtime-test'
2123
import {
2224
ITERATE_KEY,
@@ -28,7 +30,6 @@ import {
2830
Ref,
2931
effectScope
3032
} from '@vue/reactivity'
31-
import { watchPostEffect } from '../src/apiWatch'
3233

3334
// reference: https://vue-composition-api-rfc.netlify.com/api.html#watch
3435

@@ -444,6 +445,48 @@ describe('api: watch', () => {
444445
expect(result2).toBe(true)
445446
})
446447

448+
it('watchSyncEffect', async () => {
449+
const count = ref(0)
450+
const count2 = ref(0)
451+
452+
let callCount = 0
453+
let result1
454+
let result2
455+
const assertion = jest.fn(count => {
456+
callCount++
457+
// on mount, the watcher callback should be called before DOM render
458+
// on update, should be called before the count is updated
459+
const expectedDOM = callCount === 1 ? `` : `${count - 1}`
460+
result1 = serializeInner(root) === expectedDOM
461+
462+
// in a sync callback, state mutation on the next line should not have
463+
// executed yet on the 2nd call, but will be on the 3rd call.
464+
const expectedState = callCount < 3 ? 0 : 1
465+
result2 = count2.value === expectedState
466+
})
467+
468+
const Comp = {
469+
setup() {
470+
watchSyncEffect(() => {
471+
assertion(count.value)
472+
})
473+
return () => count.value
474+
}
475+
}
476+
const root = nodeOps.createElement('div')
477+
render(h(Comp), root)
478+
expect(assertion).toHaveBeenCalledTimes(1)
479+
expect(result1).toBe(true)
480+
expect(result2).toBe(true)
481+
482+
count.value++
483+
count2.value++
484+
await nextTick()
485+
expect(assertion).toHaveBeenCalledTimes(3)
486+
expect(result1).toBe(true)
487+
expect(result2).toBe(true)
488+
})
489+
447490
it('should not fire on component unmount w/ flush: post', async () => {
448491
const toggle = ref(true)
449492
const cb = jest.fn()

packages/runtime-core/src/apiWatch.ts

+13
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,19 @@ export function watchPostEffect(
9696
)
9797
}
9898

99+
export function watchSyncEffect(
100+
effect: WatchEffect,
101+
options?: DebuggerOptions
102+
) {
103+
return doWatch(
104+
effect,
105+
null,
106+
(__DEV__
107+
? Object.assign(options || {}, { flush: 'sync' })
108+
: { flush: 'sync' }) as WatchOptionsBase
109+
)
110+
}
111+
99112
// initial value for watchers to trigger on undefined initial values
100113
const INITIAL_WATCHER_VALUE = {}
101114

packages/runtime-core/src/index.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,12 @@ export {
3434
getCurrentScope,
3535
onScopeDispose
3636
} from '@vue/reactivity'
37-
export { watch, watchEffect, watchPostEffect } from './apiWatch'
37+
export {
38+
watch,
39+
watchEffect,
40+
watchPostEffect,
41+
watchSyncEffect
42+
} from './apiWatch'
3843
export {
3944
onBeforeMount,
4045
onMounted,

0 commit comments

Comments
 (0)