diff --git a/packages/runtime-core/__tests__/apiWatch.spec.ts b/packages/runtime-core/__tests__/apiWatch.spec.ts index ad8f4787145..82a4ad36811 100644 --- a/packages/runtime-core/__tests__/apiWatch.spec.ts +++ b/packages/runtime-core/__tests__/apiWatch.spec.ts @@ -27,6 +27,33 @@ describe('api: watch', () => { expect(dummy).toBe(1) }) + it('triggers when initial value is null', async () => { + const state = ref(null) + const spy = jest.fn() + watch(() => state.value, spy) + await nextTick() + expect(spy).toHaveBeenCalled() + }) + + it('triggers when initial value is undefined', async () => { + const state = ref() + const spy = jest.fn() + watch(() => state.value, spy) + await nextTick() + expect(spy).toHaveBeenCalled() + state.value = 3 + await nextTick() + expect(spy).toHaveBeenCalledTimes(2) + // testing if undefined can trigger the watcher + state.value = undefined + await nextTick() + expect(spy).toHaveBeenCalledTimes(3) + // it shouldn't trigger if the same value is set + state.value = undefined + await nextTick() + expect(spy).toHaveBeenCalledTimes(3) + }) + it('watching single source: getter', async () => { const state = reactive({ count: 0 }) let dummy diff --git a/packages/runtime-core/src/apiWatch.ts b/packages/runtime-core/src/apiWatch.ts index 74acbfd59f2..c4bcdd58f0d 100644 --- a/packages/runtime-core/src/apiWatch.ts +++ b/packages/runtime-core/src/apiWatch.ts @@ -61,6 +61,9 @@ export type StopHandle = () => void const invoke = (fn: Function) => fn() +// initial value for watchers to trigger on undefined initial values +const INITIAL_WATCHER_VALUE = {} + // overload #1: simple effect export function watch(effect: WatchEffect, options?: WatchOptions): StopHandle @@ -153,7 +156,7 @@ function doWatch( } } - let oldValue = isArray(source) ? [] : undefined + let oldValue = isArray(source) ? [] : INITIAL_WATCHER_VALUE const applyCb = cb ? () => { if (instance && instance.isUnmounted) { @@ -167,7 +170,8 @@ function doWatch( } callWithAsyncErrorHandling(cb, instance, ErrorCodes.WATCH_CALLBACK, [ newValue, - oldValue, + // pass undefined as the old value when it's changed for the first time + oldValue === INITIAL_WATCHER_VALUE ? undefined : oldValue, registerCleanup ]) oldValue = newValue