From 9bd33c7900dd3b97915fef1ca9571bc83de39425 Mon Sep 17 00:00:00 2001 From: Dan Popescu Date: Wed, 18 Nov 2020 17:12:51 +0200 Subject: [PATCH] feat(utils): add debouncedRef function - create a reference to the original value from the Vue instance, and only update the original after debouncing --- docs/src/pages/quasar-utils/other-utils.md | 27 ++++++++ ui/dev/src/pages/form/debounced-ref.vue | 72 ++++++++++++++++++++++ ui/src/utils.js | 2 + ui/src/utils/debounced-ref.js | 48 +++++++++++++++ ui/types/utils.d.ts | 6 ++ 5 files changed, 155 insertions(+) create mode 100644 ui/dev/src/pages/form/debounced-ref.vue create mode 100644 ui/src/utils/debounced-ref.js diff --git a/docs/src/pages/quasar-utils/other-utils.md b/docs/src/pages/quasar-utils/other-utils.md index 69ef6421d6bb..2f4813f553a8 100644 --- a/docs/src/pages/quasar-utils/other-utils.md +++ b/docs/src/pages/quasar-utils/other-utils.md @@ -291,6 +291,33 @@ runSequentialPromises([ /* ... */ ], { threadsNumber: 3 }) }) ``` +## Debounced Ref +Creates a new reference to an existing value in the Vue instance that will be updated in real-time, while the original value will be updated after a debouncing period. + +The new value can be used as model for a control that updates frequently (eg. QInput, QSlider, QRange, ...), while the original value will only be updated after debouncing (eg. You have a search field but you only want to perform the remote search API call 300ms after the last change in the search field). + +``` js +import { debouncedRef } from 'quasar' + +.... +data () { + return { + search: '', + debouncedSearch: debouncedRef(this, 'search', 500) + } +}, + +watch: { + debouncedSearch (newSearch, oldSearch) { + ... + } +} +``` + +::: warning +If the debounced value is an object only modify it as a whole, because it is passed by reference. If you change the value of keys inside the object the change will propagate immediatelly. +::: + ## Debounce Function If your App uses JavaScript to accomplish taxing tasks, a debounce function is essential to ensuring a given task doesn't fire so often that it bricks browser performance. Debouncing a function limits the rate at which the function can fire. diff --git a/ui/dev/src/pages/form/debounced-ref.vue b/ui/dev/src/pages/form/debounced-ref.vue new file mode 100644 index 000000000000..d075625800d5 --- /dev/null +++ b/ui/dev/src/pages/form/debounced-ref.vue @@ -0,0 +1,72 @@ + + + diff --git a/ui/src/utils.js b/ui/src/utils.js index 600ca808fb6f..25f6807adf25 100644 --- a/ui/src/utils.js +++ b/ui/src/utils.js @@ -3,6 +3,7 @@ import colors from './utils/colors.js' import copyToClipboard from './utils/copy-to-clipboard.js' import date from './utils/date.js' import debounce from './utils/debounce.js' +import debouncedRef from './utils/debounced-ref.js' import dom from './utils/dom.js' import event, { noop } from './utils/event.js' import exportFile from './utils/export-file.js' @@ -24,6 +25,7 @@ export { copyToClipboard, date, debounce, + debouncedRef, dom, event, noop, diff --git a/ui/src/utils/debounced-ref.js b/ui/src/utils/debounced-ref.js new file mode 100644 index 000000000000..91ecaa543f9a --- /dev/null +++ b/ui/src/utils/debounced-ref.js @@ -0,0 +1,48 @@ +export default function (vm, prop, wait = 250) { + const debounced = { + get value () { + const value = vm[prop] + + if (value !== debounced.propValue) { + debounced.propValue = value + debounced.internalValue = value + + debounced.timer !== void 0 && clearTimeout(debounced.timer) + debounced.timer = void 0 + } + + return debounced.internalValue + }, + + set value (value) { + if (debounced.internalValue === value) { + return + } + + debounced.internalValue = value + + debounced.timer !== void 0 && clearTimeout(debounced.timer) + debounced.timer = setTimeout(() => { + vm[prop] = value + + debounced.timer = void 0 + }, wait) + }, + + destroy () { + if (debounced.timer !== void 0) { + clearTimeout(debounced.timer) + debounced.timer = void 0 + + vm[prop] = debounced.internalValue + } + } + } + + vm.$once('hook:beforeDestroy', debounced.destroy) + + debounced.propValue = NaN + debounced.internalValue = NaN + + return debounced +} diff --git a/ui/types/utils.d.ts b/ui/types/utils.d.ts index 882566436d72..47fbd0fa6798 100644 --- a/ui/types/utils.d.ts +++ b/ui/types/utils.d.ts @@ -1,3 +1,5 @@ +import { LooseDictionary } from './ts-helpers' + export * from './utils/date'; export * from './utils/colors'; export * from './utils/dom'; @@ -14,6 +16,10 @@ export function debounce any>( wait?: number, immediate?: boolean ): F & { cancel(): void }; +export function debouncedRef(vm: T, prop: K, wait?: number): { + value: T[K], + destroy: () => void +}; export function exportFile( fileName: string, rawData: BlobPart,