From ba62ccd55d659a874ece4b26454ae31c6de72f59 Mon Sep 17 00:00:00 2001 From: Yang Mingshan Date: Mon, 18 May 2020 23:02:51 +0800 Subject: [PATCH] feat(watch): support directly watching reactive object in multiple sources with deep default (#1201) --- .../runtime-core/__tests__/apiWatch.spec.ts | 18 ++++++++ packages/runtime-core/src/apiWatch.ts | 44 ++++++++++++------- 2 files changed, 46 insertions(+), 16 deletions(-) diff --git a/packages/runtime-core/__tests__/apiWatch.spec.ts b/packages/runtime-core/__tests__/apiWatch.spec.ts index 71dfd3ca..0abf009a 100644 --- a/packages/runtime-core/__tests__/apiWatch.spec.ts +++ b/packages/runtime-core/__tests__/apiWatch.spec.ts @@ -155,12 +155,30 @@ describe('api: watch', () => { expect(dummy).toMatchObject([[2, true], [1, false]]) }) + it('watching multiple sources: reactive object (with automatic deep: true)', async () => { + const src = reactive({ count: 0 }) + let dummy + watch([src], ([state]) => { + dummy = state + // assert types + state.count === 1 + }) + src.count++ + await nextTick() + expect(dummy).toMatchObject({ count: 1 }) + }) + it('warn invalid watch source', () => { // @ts-ignore watch(1, () => {}) expect(`Invalid watch source`).toHaveBeenWarned() }) + it('warn invalid watch source: multiple sources', () => { + watch([1], () => {}) + expect(`Invalid watch source`).toHaveBeenWarned() + }) + it('stopping the watcher (effect)', 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 4fcc0a85..4d98d425 100644 --- a/packages/runtime-core/src/apiWatch.ts +++ b/packages/runtime-core/src/apiWatch.ts @@ -44,13 +44,17 @@ export type WatchCallback = ( ) => any type MapSources = { - [K in keyof T]: T[K] extends WatchSource ? V : never + [K in keyof T]: T[K] extends WatchSource + ? V + : T[K] extends object ? T[K] : never } type MapOldSources = { [K in keyof T]: T[K] extends WatchSource ? Immediate extends true ? (V | undefined) : V - : never + : T[K] extends object + ? Immediate extends true ? (T[K] | undefined) : T[K] + : never } type InvalidateCbRegistrator = (cb: () => void) => void @@ -86,7 +90,7 @@ const INITIAL_WATCHER_VALUE = {} // on position in the source array. Otherwise the values will get a union type // of all possible value types. export function watch< - T extends Readonly[]>, + T extends Readonly | object>>, Immediate extends Readonly = false >( sources: T, @@ -147,17 +151,31 @@ function doWatch( } } + const warnInvalidSource = (s: unknown) => { + warn( + `Invalid watch source: `, + s, + `A watch source can only be a getter/effect function, a ref, ` + + `a reactive object, or an array of these types.` + ) + } + const instance = currentInstance let getter: () => any if (isArray(source)) { getter = () => - source.map( - s => - isRef(s) - ? s.value - : callWithErrorHandling(s, instance, ErrorCodes.WATCH_GETTER) - ) + source.map(s => { + if (isRef(s)) { + return s.value + } else if (isReactive(s)) { + return traverse(s) + } else if (isFunction(s)) { + return callWithErrorHandling(s, instance, ErrorCodes.WATCH_GETTER) + } else { + __DEV__ && warnInvalidSource(s) + } + }) } else if (isRef(source)) { getter = () => source.value } else if (isReactive(source)) { @@ -187,13 +205,7 @@ function doWatch( } } else { getter = NOOP - __DEV__ && - warn( - `Invalid watch source: `, - source, - `A watch source can only be a getter/effect function, a ref, ` + - `a reactive object, or an array of these types.` - ) + __DEV__ && warnInvalidSource(source) } if (cb && deep) {