Skip to content

Commit

Permalink
feat(watch): support directly watching reactive object in multiple so…
Browse files Browse the repository at this point in the history
…urces with deep default (#1201)
  • Loading branch information
yangmingshan authored May 18, 2020
1 parent 83b7158 commit ba62ccd
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 16 deletions.
18 changes: 18 additions & 0 deletions packages/runtime-core/__tests__/apiWatch.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
44 changes: 28 additions & 16 deletions packages/runtime-core/src/apiWatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,17 @@ export type WatchCallback<V = any, OV = any> = (
) => any

type MapSources<T> = {
[K in keyof T]: T[K] extends WatchSource<infer V> ? V : never
[K in keyof T]: T[K] extends WatchSource<infer V>
? V
: T[K] extends object ? T[K] : never
}

type MapOldSources<T, Immediate> = {
[K in keyof T]: T[K] extends WatchSource<infer V>
? 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
Expand Down Expand Up @@ -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<WatchSource<unknown>[]>,
T extends Readonly<Array<WatchSource<unknown> | object>>,
Immediate extends Readonly<boolean> = false
>(
sources: T,
Expand Down Expand Up @@ -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)) {
Expand Down Expand Up @@ -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) {
Expand Down

0 comments on commit ba62ccd

Please sign in to comment.