VUE

useDeepWatch Composable

Enhanced deep watch with debounce, immediate execution and cleanup

Vue3ComposablesWatchReactiveDeepWatch

Code

import { watch, type Ref, type WatchSource, onUnmounted } from 'vue';

export function useDeepWatch<T>(
  source: WatchSource<T> | WatchSource<T>[],
  callback: (newVal: T, oldVal: T) => void | Promise<void>,
  options: {
    deep?: boolean;
    immediate?: boolean;
    debounce?: number;
    flush?: 'pre' | 'post' | 'sync';
  } = { deep: true, immediate: false, debounce: 0 }
) {
  let debounceTimeout: ReturnType<typeof setTimeout> | null = null;
  let cleanupFn: (() => void) | null = null;

  // Wrapped callback with debounce
  const wrappedCallback = (newVal: T, oldVal: T) => {
    __TOKEN_28__ (options.debounce && options.debounce > 0) {
      __TOKEN_29__ (debounceTimeout) clearTimeout(debounceTimeout);
      debounceTimeout = setTimeout(() => {
        callback(newVal, oldVal);
      }, options.debounce);
    } else {
      callback(newVal, oldVal);
    }
  };

  // Create watcher
  const stopWatch = watch(
    source as any,
    wrappedCallback,
    {
      deep: options.deep ?? true,
      immediate: options.immediate ?? false,
      flush: options.flush ?? 'pre'
    }
  );

  // Cleanup function
  const cleanup = () => {
    stopWatch();
    __TOKEN_33__ (debounceTimeout) clearTimeout(debounceTimeout);
    __TOKEN_34__ (cleanupFn) cleanupFn();
  };

  // Register cleanup on unmount
  onUnmounted(cleanup);

  // Allow setting custom cleanup
  const setCleanup = (fn: () => void) => {
    cleanupFn = fn;
  };

  return {
    stop: stopWatch,
    cleanup,
    setCleanup
  };
}

// Usage example
// const formData = ref({ name: '', address: { city: '', zip: '' } });
// const { cleanup } = useDeepWatch(
//   formData,
//   (newVal, oldVal) => console.log('Form changed:', newVal),
//   { debounce: 300, immediate: true }
// );
// cleanup(); // Manual cleanup