Debounced Watch in Vue with TypeScript
Creating a debounced watch in Vue allows you to execute a function only after a certain period of inactivity, preventing excessive function calls triggered by rapid changes in a reactive variable. This is particularly useful for scenarios like expensive calculations, API calls, or updating UI elements that should only happen after the user has finished interacting with a form or input. This challenge asks you to implement a composable function that provides a debounced version of Vue's watch API.
Problem Description
You need to create a Vue composable function called useDebouncedWatch that takes a reactive variable, a debouncing delay (in milliseconds), and a callback function as arguments. This composable should wrap Vue's watch API to ensure the callback function is only executed after the specified delay has passed without any further changes to the watched variable.
Key Requirements:
- Composable Function: The solution must be a Vue composable function (
useDebouncedWatch). - Debouncing: The callback function should be debounced, meaning it will only execute after the watched variable has remained unchanged for the specified delay.
- Reactive Variable: The function must correctly watch a reactive variable provided as an argument.
- Delay: The debouncing delay must be configurable.
- Callback Function: The function must execute the provided callback function when the debouncing period has elapsed.
- Cleanup: The composable should properly clean up the debouncing timer when the component unmounts to prevent memory leaks.
Expected Behavior:
- When
useDebouncedWatchis called, it should set up a Vuewatcheffect. - Each time the watched variable changes, the debouncing timer should be reset.
- If the watched variable remains unchanged for the specified delay, the callback function should be executed.
- When the component unmounts, the debouncing timer should be cleared.
Edge Cases to Consider:
- Initial Value: The callback function should not be executed immediately when the component mounts, even if the initial value of the watched variable is different from the value after the first change.
- Rapid Changes: If the watched variable changes rapidly, the callback function should only execute after the last change has remained unchanged for the specified delay.
- Zero Delay: A delay of 0 should execute the callback immediately after each change.
- Unmounting: Ensure the debouncing timer is cleared when the component is unmounted to prevent errors and memory leaks.
Examples
Example 1:
Input:
- reactiveVariable: ref(0)
- delay: 500
- callback: (value) => console.log('Value after debounce:', value)
Actions:
- reactiveVariable.value = 1
- reactiveVariable.value = 2 (after 100ms)
- reactiveVariable.value = 3 (after 200ms)
- reactiveVariable.value = 4 (after 300ms)
- reactiveVariable.value = 5 (after 400ms)
- reactiveVariable.value = 6 (after 500ms)
- reactiveVariable.value = 7 (after 600ms)
Output:
- console.log('Value after debounce:', 6)
Explanation:
The callback function will only be executed after 500ms of inactivity. The rapid changes reset the timer each time. The final change to 6 allows the timer to expire and the callback to execute.
Example 2:
Input:
- reactiveVariable: ref("hello")
- delay: 200
- callback: (value) => console.log('String value:', value)
Actions:
- reactiveVariable.value = "world"
- reactiveVariable.value = "vue"
- reactiveVariable.value = "typescript"
Output:
- console.log('String value:', "typescript")
Explanation:
Similar to Example 1, the callback is executed after 200ms of inactivity following the last change to "typescript".
Example 3:
Input:
- reactiveVariable: ref(true)
- delay: 0
- callback: (value) => console.log('Boolean value:', value)
Actions:
- reactiveVariable.value = false
- reactiveVariable.value = true
Output:
- console.log('Boolean value:', false)
- console.log('Boolean value:', true)
Explanation:
With a delay of 0, the callback is executed immediately after each change.
Constraints
- The delay must be a non-negative number (integer or float).
- The reactive variable can be any reactive value managed by Vue (e.g.,
ref,reactive). - The callback function must be a function that accepts the current value of the reactive variable as an argument.
- The composable should be compatible with Vue 3.
- The solution should be written in TypeScript.
Notes
- Consider using
setTimeoutorsetIntervalto implement the debouncing logic. Remember to clear the timer appropriately. - Think about how to handle the cleanup when the component unmounts.
onUnmountedlifecycle hook is useful here. - The
watchAPI in Vue provides a convenient way to react to changes in a reactive variable. Leverage this API to simplify your solution. - Focus on creating a reusable and well-tested composable function.