feat(watch): support directly watching reactive object with deep default
Also warn invalid watch sources close #1110
This commit is contained in:
parent
64ef7c76bf
commit
6b33cc4229
@ -103,6 +103,19 @@ describe('api: watch', () => {
|
||||
expect(dummy).toMatchObject([1, 0])
|
||||
})
|
||||
|
||||
it('directly watching reactive object (with automatic deep: true)', async () => {
|
||||
const src = reactive({
|
||||
count: 0
|
||||
})
|
||||
let dummy
|
||||
watch(src, ({ count }) => {
|
||||
dummy = count
|
||||
})
|
||||
src.count++
|
||||
await nextTick()
|
||||
expect(dummy).toBe(1)
|
||||
})
|
||||
|
||||
it('watching multiple sources', async () => {
|
||||
const state = reactive({ count: 1 })
|
||||
const count = ref(1)
|
||||
@ -142,6 +155,12 @@ describe('api: watch', () => {
|
||||
expect(dummy).toMatchObject([[2, true], [1, false]])
|
||||
})
|
||||
|
||||
it('warn invalid watch source', () => {
|
||||
// @ts-ignore
|
||||
watch(1, () => {})
|
||||
expect(`Invalid watch source`).toHaveBeenWarned()
|
||||
})
|
||||
|
||||
it('stopping the watcher (effect)', async () => {
|
||||
const state = reactive({ count: 0 })
|
||||
let dummy
|
||||
|
@ -4,7 +4,8 @@ import {
|
||||
isRef,
|
||||
Ref,
|
||||
ComputedRef,
|
||||
ReactiveEffectOptions
|
||||
ReactiveEffectOptions,
|
||||
isReactive
|
||||
} from '@vue/reactivity'
|
||||
import { queueJob } from './scheduler'
|
||||
import {
|
||||
@ -80,14 +81,7 @@ export function watchEffect(
|
||||
// initial value for watchers to trigger on undefined initial values
|
||||
const INITIAL_WATCHER_VALUE = {}
|
||||
|
||||
// overload #1: single source + cb
|
||||
export function watch<T, Immediate extends Readonly<boolean> = false>(
|
||||
source: WatchSource<T>,
|
||||
cb: WatchCallback<T, Immediate extends true ? (T | undefined) : T>,
|
||||
options?: WatchOptions<Immediate>
|
||||
): WatchStopHandle
|
||||
|
||||
// overload #2: array of multiple sources + cb
|
||||
// overload #1: array of multiple sources + cb
|
||||
// Readonly constraint helps the callback to correctly infer value types based
|
||||
// on position in the source array. Otherwise the values will get a union type
|
||||
// of all possible value types.
|
||||
@ -100,6 +94,23 @@ export function watch<
|
||||
options?: WatchOptions<Immediate>
|
||||
): WatchStopHandle
|
||||
|
||||
// overload #2: single source + cb
|
||||
export function watch<T, Immediate extends Readonly<boolean> = false>(
|
||||
source: WatchSource<T>,
|
||||
cb: WatchCallback<T, Immediate extends true ? (T | undefined) : T>,
|
||||
options?: WatchOptions<Immediate>
|
||||
): WatchStopHandle
|
||||
|
||||
// overload #3: watching reactive object w/ cb
|
||||
export function watch<
|
||||
T extends object,
|
||||
Immediate extends Readonly<boolean> = false
|
||||
>(
|
||||
source: T,
|
||||
cb: WatchCallback<T, Immediate extends true ? (T | undefined) : T>,
|
||||
options?: WatchOptions<Immediate>
|
||||
): WatchStopHandle
|
||||
|
||||
// implementation
|
||||
export function watch<T = any>(
|
||||
source: WatchSource<T> | WatchSource<T>[],
|
||||
@ -149,26 +160,39 @@ function doWatch(
|
||||
)
|
||||
} else if (isRef(source)) {
|
||||
getter = () => source.value
|
||||
} else if (cb) {
|
||||
// getter with cb
|
||||
getter = () =>
|
||||
callWithErrorHandling(source, instance, ErrorCodes.WATCH_GETTER)
|
||||
} else {
|
||||
// no cb -> simple effect
|
||||
getter = () => {
|
||||
if (instance && instance.isUnmounted) {
|
||||
return
|
||||
} else if (isReactive(source)) {
|
||||
getter = () => source
|
||||
deep = true
|
||||
} else if (isFunction(source)) {
|
||||
if (cb) {
|
||||
// getter with cb
|
||||
getter = () =>
|
||||
callWithErrorHandling(source, instance, ErrorCodes.WATCH_GETTER)
|
||||
} else {
|
||||
// no cb -> simple effect
|
||||
getter = () => {
|
||||
if (instance && instance.isUnmounted) {
|
||||
return
|
||||
}
|
||||
if (cleanup) {
|
||||
cleanup()
|
||||
}
|
||||
return callWithErrorHandling(
|
||||
source,
|
||||
instance,
|
||||
ErrorCodes.WATCH_CALLBACK,
|
||||
[onInvalidate]
|
||||
)
|
||||
}
|
||||
if (cleanup) {
|
||||
cleanup()
|
||||
}
|
||||
return callWithErrorHandling(
|
||||
source,
|
||||
instance,
|
||||
ErrorCodes.WATCH_CALLBACK,
|
||||
[onInvalidate]
|
||||
)
|
||||
}
|
||||
} else {
|
||||
getter = NOOP
|
||||
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.`
|
||||
)
|
||||
}
|
||||
|
||||
if (cb && deep) {
|
||||
|
Loading…
Reference in New Issue
Block a user