I was looking into the debouncing option you raised for useStorage and I wanted to first see if the same can be achieved in a more flexible way.
Please take this as a dump of ideas and not a definite API. I thought about this while trying to design the API for other composables, and I want to share it in case it is useful in some way for vueuse.
The problem that I see with adding a denounce option is that we may also want to add a throttle one later (or pausable, or other ideas we may have later). We are adding complexity to the main composable, and users will need to pay for this even if they do not use it. There are other composables like useRefHistory where users may expect the same functionality.
So, what if the API lets the user define the filtering function to be used? I think you called it updater in the useDebounce implementation. I am using filter
as the option name just because it is called like that in Rx, but I am not married with the name. The signature for filter is ( (fn) => void ) => void
const count = useStorage('counter', 0, { filter: debounceFilter(200) })
const count = useStorage('counter', 0, { filter: throttleFilter(300) })
const { filter, pause, resume } = pausableFilter()
const count = useStorage('counter', 0, { filter })
debounceFilter
could be implemented as:
export function debounceFilter(delay = 200) {
return (fn) => useDebounceFn(() => fn(), delay)
}
The idea is that inside useStorage, instead of a direct watch(data, commit, options)
we use watch(data, filter(commit), options)
So, the filter
option gives users the possibility to decide which triggered values to accept from the watch.
Also, if we have these filters in the lib, we can also implement useDebounceRef, useThrotleRef, etc using a more general useFilteredRef
export function pausableFilter() {
const tracking = ref(true)
function pause() {
tracking.value = false
}
function resume() {
tracking.value = true
}
function filter(fn) {
return () => {
if( tracking.value ) {
fn()
}
}
return { tracking, pause, resume, filter }
}
The filters are composable also, allowing users to create pausable throttled storage for example
const { filter, pause, resume } = pausableFilter()
const count = useStorage('counter', 0, { filter: filter(throttleFilter(300)) })
And they can actually create their own filters, pausing or delaying according to their app needs (using other app refs, etc)
I also wonder if we could simplify useRefHistory by removing the pausable part out of the main composable, and letting users choose if they want that functionality
const data = ref(0)
const { filter, pause, resume } = pausableFilter()
const { undo, redo } = useRefHistory(data, { filter })
It is also good that we are not adding an extra watch for every filter. Another possible API for composables that are watching an input ref is to expect the user to give us a filtered ref, something like:
const data = ref(0)
const { pausableRef, pause, resume } = usePausableRef(data)
const { undo, redo } = useRefHistory(pausableRef)
This only works with API that are tracking an input ref (so we can not use it in useStorage), but we could offer this possibility also.
I think there are other possible APIs we could explore. I thought about letting the user give us the watch
function to be used, but then you may need to use the same options (flush, deep) in several places.
For common cases, we can also bundle filters for convenience like:
const data = ref(0)
const { undo, redo, pause, resume } = usePausableRefHistory(data)
implemented as
export function usePausableRefHistory(ref, options) {
const { filter, pause, resume } = pausableFilter()
return { ...useRefHistory(data, { filter }), pause, resume }
}
So we can still offer the general composable and specialized ones, and users will only pay for what they use. Take the pausable filter extraction as an example to extrapolate to other cases, because the current API allows users to commit using resume(true)
. There may be a better API to also support external triggers. Or the filter may also receive the value, so you can implement validation filters, remove values if they are negative, etc. Or that maybe another option orthogonal to this one.
What do you think? Is something like a filter option worth exploring so we do not need to implement debounce/throttle/pausable in each composable?
Disclaimer: I do not know if I can actually do this myself but looks interesting, so at least I wanted to share it ๐