module author Surmon <https://github.com/surmon-china>
  import { useState as useReactState } from 'react' ; import { isRef , isReactive , Ref , EffectScheduler , ReactiveEffect , } from '@vue/reactivity' ; import { onBeforeUnmount } from '../lifecycle' ; import type { ComputedRef } from '../reactivity' ; import { logger , callWithErrorHandling , callWithAsyncErrorHandling } from '../logger' ; import { hasChanged , isArray , isFunction } from '../utils' ; import { traverse , WATCH_GETTER_ERROR , WATCH_CLEANUP_ERROR , WATCH_CALLBACK_ERROR , } from './patch' ; import { WatchEffectOptions , WatchStopHandle , InvalidateCallbackRegistrator , } from './watchEffect' ; const NOOP_FN = ( ) => { } ; const INITIAL_WATCHER_VALUE = { } ; const warnInvalidSource = ( source ) => { logger . 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.` ) ; } ; export interface WatchOptions < Immediate = boolean > extends WatchEffectOptions { immediate ? : Immediate ; deep ? : boolean ; ssr ? : boolean ; } export type WatchFn < T > = ( ) => T ; export type WatchSource < T = any > = Ref < T > | ComputedRef < T > | WatchFn < T > ; export type WatchCallback < Value = any , OldValue = any > = ( value : Value , oldValue : OldValue , onInvalidate : InvalidateCallbackRegistrator ) => any ; type MultiWatchSources = ( WatchSource < unknown > | object ) [ ] ; type MapSources < T , Immediate > = { [ K in keyof T ] : T [ K ] extends WatchSource < infer V > ? Immediate extends true ? V | undefined : V : T [ K ] extends object ? Immediate extends true ? T [ K ] | undefined : T [ K ] : never ; } ; export function watch < T extends MultiWatchSources , Immediate extends Readonly < boolean > = false > ( sources , cb , options ? ) ; description example export function watch < T extends Readonly < MultiWatchSources > , Immediate extends Readonly < boolean > = false > ( source , cb , options ? ) ; export function watch < T , Immediate extends Readonly < boolean > = false > ( source , cb , options ? ) ; export function watch < T extends object , Immediate extends Readonly < boolean > = false > ( source , cb , options ? ) ; export function watch < T = any , Immediate extends Readonly < boolean > = false > ( source , callback , options = { } ) { if ( ! isFunction ( callback ) ) { logger . warn ( `\`watch\` only supports \`watch(source, cb, options?) signature.` ) ; } const { immediate , deep , ssr , onTrack , onTrigger } = options ; let getter ; let effector ; let isDeep = deep ; let isMultiSource = false ; let forceTrigger = false ; let cleanup ; let onInvalidate = ( fn ) => { cleanup = effector . onStop = ( ) => { callWithErrorHandling ( fn , WATCH_CLEANUP_ERROR ) ; } ; } ; if ( isFunction ( source ) ) { getter = ( ) => callWithErrorHandling ( source , WATCH_GETTER_ERROR ) ; } else if ( isRef ( source ) ) { forceTrigger = Boolean ( ( source as any ) . _shallow ) ; getter = ( ) => source . value ; } else if ( isReactive ( source ) ) { isDeep = true ; getter = ( ) => source ; } else if ( isArray ( source ) ) { isMultiSource = true ; forceTrigger = source . some ( isReactive ) ; getter = ( ) => { return source . map ( ( _source ) => { if ( isRef ( _source ) ) { return _source . value ; } else if ( isReactive ( _source ) ) { return traverse ( _source ) ; } else if ( isFunction ( _source ) ) { return callWithErrorHandling ( _source , WATCH_GETTER_ERROR ) ; } else { warnInvalidSource ( _source ) ; return null ; } } ) ; } ; } else { getter = NOOP_FN ; warnInvalidSource ( source ) ; } if ( isDeep ) { const _getter = getter ; getter = ( ) => traverse ( _getter ( ) ) ; } if ( ssr ) { onInvalidate = NOOP_FN ; if ( immediate ) { callWithAsyncErrorHandling ( callback , WATCH_CALLBACK_ERROR , [ getter ( ) , isMultiSource ? [ ] : undefined , onInvalidate , ] ) ; } return NOOP_FN ; } let oldValue = isMultiSource ? [ ] : INITIAL_WATCHER_VALUE ; const scheduler = ( ) => { if ( ! effector . active ) { return ; } const newValue = effector . run ( ) ; if ( isDeep || forceTrigger || ( isMultiSource ? ( newValue as unknown as any [ ] ) . some ( ( value , i ) => hasChanged ( value , ( oldValue as any [ ] ) [ i ] ) ) : hasChanged ( newValue , oldValue ) ) ) { cleanup ?. ( ) ; callWithAsyncErrorHandling ( callback , WATCH_CALLBACK_ERROR , [ newValue , oldValue === INITIAL_WATCHER_VALUE ? undefined : oldValue , onInvalidate , ] ) ; oldValue = newValue ; } } ; effector = new ReactiveEffect < T > ( getter , scheduler ) ; effector . onTrack = onTrack ; effector . onTrigger = onTrigger ; if ( immediate ) { scheduler ( ) ; } else { oldValue = effector . run ( ) ; } return ( ) => effector . stop ( ) ; } export function useWatch < T extends MultiWatchSources , Immediate extends Readonly < boolean > = false > ( sources , cb , options ? ) ; export function useWatch < T extends Readonly < MultiWatchSources > , Immediate extends Readonly < boolean > = false > ( source , cb , options ? ) ; export function useWatch < T , Immediate extends Readonly < boolean > = false > ( source , cb , options ? ) ; export function useWatch < T extends object , Immediate extends Readonly < boolean > = false > ( source , cb , options ? ) ; export function useWatch < T = any , Immediate extends Readonly < boolean > = false > ( source , callback , options = { } ) { const [ stopHandler ] = useReactState ( ( ) => watch ( source as any , callback , options ) ) ; onBeforeUnmount ( ( ) => stopHandler ?. ( ) ) ; return stopHandler ; }