Hone logo
Hone
Problems

Reactive Data Watcher with Immediate Execution in Vue.js (TypeScript)

This challenge focuses on creating a custom Vue.js watcher that executes immediately upon creation and then continues to react to changes in the watched data. Immediate watchers are useful when you need to perform an action as soon as a component is mounted or a data property is initialized, rather than waiting for a subsequent change. This is a common pattern for things like fetching initial data or setting up initial state based on a reactive variable.

Problem Description

You are tasked with creating a useImmediateWatch composable function in Vue.js using TypeScript. This composable should take two arguments:

  1. source: A reactive data source (e.g., a ref, reactive object property).
  2. callback: A function to be executed immediately upon creation and whenever the source changes.

The useImmediateWatch composable should:

  • Immediately execute the callback function when it's first called.
  • Add a watcher to the source that executes the callback function whenever the source changes.
  • Return a function to unwatch the source, allowing the watcher to be stopped.

Key Requirements:

  • The composable must be written in TypeScript.
  • It should correctly handle different types of reactive sources (refs, reactive objects).
  • The callback function should receive the new value of the source as its argument.
  • The returned unwatch function should properly remove the watcher.

Expected Behavior:

When useImmediateWatch is called, the callback should execute once immediately. Subsequent changes to the source should trigger the callback again. Calling the returned unwatch function should stop the watcher and prevent further executions of the callback.

Edge Cases to Consider:

  • What happens if the source is already equal to its initial value? The callback should still execute immediately.
  • What happens if the source is an object? The watcher should track changes to the object's properties.
  • What happens if the callback throws an error? The watcher should continue to function.
  • What happens if the component using the watcher is unmounted? The unwatch function should prevent memory leaks.

Examples

Example 1:

Input:
source: ref<number>(0)
callback: (newValue: number) => console.log("New value:", newValue)

Output:
console.log("New value: 0")  // Immediate execution
source.value = 1
console.log("New value: 1")  // Watcher execution
source.value = 1
// No output (value hasn't changed)

Example 2:

Input:
source: reactive({ count: 0 })
callback: (newValue: { count: number }) => console.log("Count:", newValue.count)

Output:
console.log("Count: 0") // Immediate execution
source.count = 1
console.log("Count: 1") // Watcher execution
source.count = 2
console.log("Count: 2") // Watcher execution

Example 3: (Edge Case - Unmounting)

Input:
source: ref<string>("initial")
callback: (newValue: string) => console.log("Value:", newValue)
unwatchFunction = useImmediateWatch(source, callback)

// Component is mounted, callback executes immediately: console.log("Value: initial")
// Component is unmounted
unwatchFunction() // Call unwatch function
source.value = "new value" // Changing source after unmounting should not trigger callback

Constraints

  • The composable must be implemented using Vue 3's Composition API.
  • The code must be written in TypeScript.
  • The callback function should be executed asynchronously (using nextTick) to ensure that the DOM has been updated before the callback is executed. This is important for scenarios where the callback modifies the DOM.
  • The unwatch function must properly remove the watcher to prevent memory leaks.
  • The solution should be concise and readable.

Notes

Consider using watch and nextTick from Vue's API. Think about how to handle different reactive types gracefully. The immediate execution is the key differentiator from a standard Vue watcher. Remember to return a function that allows the watcher to be stopped. Focus on creating a reusable and robust composable.

Loading editor...
typescript